DEV Community

loading...

XML Parsing in Swift

Midhet Sulemani
iOS Developer by profession. Interested in UX Design, AI and Robotics. I also enjoy watching random YouTube videos, reading, going out for walks and doing some graphic design on the side.
・3 min read

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 😂

So here's a simple parsing tutorial to parse XML in your Swift Data Models. Download the starter project from here

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

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

var xmlDict = [String: Any]()
var xmlDictArr = [[String: Any]]()
var currentElement = ""
Enter fullscreen mode Exit fullscreen mode

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:

let xmlResponseData = Bundle.main.getFileData("catfacts.xml")
let parser = XMLParser(data: xmlResponseData)
parser.delegate = self
parser.parse()
Enter fullscreen mode Exit fullscreen mode

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:

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
    if elementName == "element" {
        xmlDict = [:]
    } else {
        currentElement = elementName
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

func parser(_ parser: XMLParser, foundCharacters string: String) {
    if !string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
        if xmlDict[currentElement] == nil {
               xmlDict.updateValue(string, forKey: currentElement)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In this method, we are notified of the values of the element tag through the parameter of string

The third method is as follows:

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    if elementName == "element" {
            xmlDictArr.append(xmlDict)
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

func parserDidEndDocument(_ parser: XMLParser) {
     parsingCompleted()
}
Enter fullscreen mode Exit fullscreen mode

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:

func parsingCompleted() {
    self.facts = self.xmlDictArr.map { Fact(details: $0) }
    self.updateUI()
}
Enter fullscreen mode Exit fullscreen mode

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:

init(details: [String: Any]) {
    id = details["_id"] as? String ?? ""
    fact = details["text"] as? String ?? ""
}
Enter fullscreen mode Exit fullscreen mode

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

If you would need any help, the complete project is linked here

Discussion (0)