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!
In the starter project we will first add these three variables:
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:
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:
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:
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:
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:
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:
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:
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!
If you would need any help, the complete project is linked here
Top comments (0)