I will be explaining how to create an XML parser, parse a simple XML document from URL and convert it into objects in Swift 3. Since I have been learning iOS lately and have not found a clear answer with a good explanation, I will do it myself here.
Let’s start by describing our XML structure. Imagine that we have a web service that will return a list of houses and their locations as a result. The XML could in this case look like this:
There are three steps that we need to do in order to finish our task – get contents of URL, parse the structure using our XML parser, create a custom House class and save custom objects.
1. Get contents of URL
There is a lot of ways to parse XML content from specific URL. I’m going to describe a very, very simple way that does not require a lot of coding. There is a class called XMLParser that will let you parse the URL without having to create an URLSession, or anything similar. All you have to do is specify an URL and create a new object of this class.
var parser = XMLParser() override func viewDidLoad() { super.viewDidLoad() let urlString = URL(string: "http://your.address.here/file.php?parameter=value") self.parser = XMLParser(contentsOf: urlString!)! self.parser.delegate = self let success:Bool = self.parser.parse() if success { print("success") } else { print("parse failure!") } }
As you might notice, we already used the delegate parameter, which will connect the parser with the class. In the second step, we will use the methods from XMLParserDelegate class, but before we do, you have to implement this class into yours by doing this:
class ViewController: UIViewController, XMLParserDelegate { ... }
2. Create XML Parser & Parse the XML structure
So far we have parsed the contents of URL address and set up our class in order to use the XMLParserDelegate. Now we have to implement its functions and specify their functionality. We will implement four functions:
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { } func parser(_ parser: XMLParser, foundCharacters string: String) { } func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { }
The first function (didStartElement) is called whenever the XML Parser came across a new element in the content. For example, it would be called anytime an element <house> is found. The second function (didEndElement) is called whenever any element is closed, f.e. </house>. The third function (foundCharacters) is called when any characters are found between these two tags. And the last function is called when a parsing error has occured.
We do not care too much about the second and third function, because we do not really need it. The way we are going to do it is that anytime we come across the element <house> we get its children and create an object of our custom House class. The fourth function will serve us only for printing out the error that has occurred, so it will look like this:
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { print("failure error: ", parseError) }
The first function is the most important. All the magic will happen inside of it. We do not need to parse the Result element, as all we care about is Houses. So we will compare the elementName parameter of the function to “House”. The function also provides us with another parameter – attributeDict. It is a collection (of 2D type [String:String]) of the element’s children. Looping through them and checking their element names is a way to go.
if(elementName=="House") { for string in attributeDict { switch string.key { case "id": ... break case "name": ... break case "location": ... break default: break } } }
That is all for this step. What to do next – create a custom House class.
3. Create a custom House class
In order to be able to save every house and work with it, we will create its custom class, from which we will create a new object for each house.
class House { var id:Int = 0 var name:String = "" var location:String = "" }
You might create your constructor and other functions, but this is all we need for now.
4. Save custom objects
var housearray = [House]() func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { if(elementName=="House") { let house = House() for string in attributeDict { let strvalue = string.value as NSString switch string.key { case "id": house.id = strvalue.integerValue break case "name": house.name = strvalue as String break case "location": house.location = strvalue as String break default: break } } housearray.append(house) } }
And that is all. You should now be able to parse any XML document. All you have to do is to modify the function above based on your requirements.
For more articles from iOS category, proceed here.