XML Parsing in Swift
Tags: parsing, xml, swift, ios
Published: 2021-04-22 12:25:33

A few years ago I had tried to look for some resources to parse XML in Swift for an interview test that I had received, without avail. Now I know this isn't the most common or developer friendly solution for parsing web service data but more often than not, due to security and other business decisions, the web and mobile developers need to find their own solutions towards parsing issues :joy:

So here's a simple parsing tutorial to parse XML in your Swift Data Models. Download the starter project from [here](https://github.com/midhetfatema94/CatFactsStarter)

In this project we will be going over _multilevel XML documents_ which will map into our array of data models. Will also be learning Cat Facts!

![Cat Facts GIF](https://media.giphy.com/media/44jJQXPAbvKfK/giphy.gif)

In the starter project we will first add these three variables:

```swift
var xmlDict = [String: Any]()
var xmlDictArr = [[String: Any]]()
var currentElement = ""
```

`xmlDict` is going to keep a record of the current parsed element. `xmlDictArr` is going to keep a record of any arrays of objects that you may encounter while parsing. `currentElement` will tell you the name of the element being parsed.

In the starter project, you will see a XML response file called `catfacts.xml`. We will be using this response file to display the cat facts in the Label. Let's populate the `loadCatFacts()` method with:

```swift
let xmlResponseData = Bundle.main.getFileData("catfacts.xml")
let parser = XMLParser(data: xmlResponseData)
parser.delegate = self
parser.parse()
```

We need to add the protocol `XMLParserDelegate` to the `ViewController` class for implementation of our parsing methods. 

The first method in the protocol will be:

```swift
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
    if elementName == "element" {
        xmlDict = [:]
    } else {
        currentElement = elementName
    }
}
```
In this method we will be notified of the start of the process and the start of each element tag.

The second method will be as follows:

```swift
func parser(_ parser: XMLParser, foundCharacters string: String) {
    if !string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
        if xmlDict[currentElement] == nil {
               xmlDict.updateValue(string, forKey: currentElement)
        }
    }
}
```
In this method, we are notified of the values of the element tag through the parameter of `string`

The third method is as follows:

```swift
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    if elementName == "element" {
            xmlDictArr.append(xmlDict)
    }
}
```
This method is called on encountering the closing tag of an element. Whether it is the current element or not, is for us to judge. 

The last method that we need to use is as follows:

```swift 
func parserDidEndDocument(_ parser: XMLParser) {
     parsingCompleted()
}
```
This method is called when the complete document has ended and the parser has encountered a closing `root` tag.

In the `parserDidEndDocument` method we can call our user defined method where we map the dictionary we have created into the data model we require. So the `parsingCompleted()` method will be written like so:

```swift
func parsingCompleted() {
    self.facts = self.xmlDictArr.map { Fact(details: $0) }
    self.updateUI()
}
```

The last step towards the completion of this project will be tying up the loose ends. 

This will be done by customizing the `init()` call in out data model struct:

```swift
init(details: [String: Any]) {
    id = details["_id"] as? String ?? ""
    fact = details["text"] as? String ?? ""
}
```

And by adding the `updateUI()` method in the `factButton` `@IBAction` outlet. 

Now Build and Run and let the magic unfold!

Hope you're doing the Happy Dance along with me!

![Happy Cat Dance](https://media.giphy.com/media/xcfz72KwMHZ7O/giphy.gif)

If you would need any help, the complete project is linked [here](https://github.com/midhetfatema94/CatFacts)