3.3 Implementing NSXMLParserDelegate
When it comes to using the NSXMLParser, it may handle a lot of the heavily lifting in terms of downloading data and parsing XML, but we still need to tell it what to do with that data. Let's get started.
1.Introduction3 lessons, 06:16
2.Setting Up the Project5 lessons, 1:00:13
3.Processing Data3 lessons, 37:52
4.Interacting With the UI6 lessons, 41:27
5.Core Data6 lessons, 44:36
6.Conclusion1 lesson, 02:47
3.3 Implementing NSXMLParserDelegate
We have agreed that our FeedsTableViewController is actually gonna be its delegate so we had to specify that in the class declaration. So now we need to actually provide some of the implementation for that particular delegate, so we can handle some of these events that are gonna happen during the process of downloading and parsing our feed. Now to do that, we're gonna actually gonna pop down into our class here. We're gonna start typing in parser, and you're gonna see a fairly lengthy list of functions here and these are going to be the delegate functions that we can kind of tap into to know when certain things are happening. So we're gonna need to use three of these. We're gonna need to use the third one down which is the didStartElement. So we'll just go ahead and hit Tab here. And then as you can see here, it popped in our function, and took us into the center where we have this little code thing where we can start actually typing in code. I'm just gonna delete that for the moment, and we're gonna come back to that function in just a second. So we'll start typing parser again, the next one we're gonna need to know is we're gonna need foundCharacters. So I'll hit Tab there, we'll delete that little code block. And for the third one, we're actually going to need didEndElement. So these are the three functions that we're going to tap into when we're actually becoming the delegate of the x amount parser. So the first one didStartElement, is actually going to happen when the x amount parse or notices that has finished parsing out a particular element of our XML file so then we can tap into this and see what's going on and actually doing some logic. Then we have foundCharacters which is where we're going to be able to kind of parse out and take a look at what are some of the values that are within that element in the form of say attributes which are gonna become important when we're looking for things like the title of articles or the lengths that are found within articles or the publication date. And then finally, we have didEndElement which is going to tell us when we finished parsing out a particular element which is gonna become important when we know, to allow us to know that we have either finished parsing out the parent level of our feed versus the child of our feed. So within our application, we're talking about a feed and an article. Now if you've never looked at RSS feeds before which is kinda what we're gonna deal with within this particular application. With an RSS feed, you have the parent level known as a channel. And a channel is going to be a feed in our case. And then a channel is made up of child elements or items. In our case, those items are gonna map to articles. So I've just kinda taken the liberty to change those, that terminology from a model perspective to make more sense to our particular application. But just remember, in our application, a feed is going to represent an RSS channel and an article is gonna represent an RSS item. So that's fairly, fairly straightforward I think. So now we're gonna go ahead and start writing in some logic that's gonna help us to tap into this functionality and actually get some work done. So the first thing we wanna know within our didStartElement is whether or not we are actually within a channel or within an item because our parsing is gonna be done a little differently. So the first thing we will check is to see that didStartElement, we get to know the element name. So we're gonna know what the particular element name is that we're working with. So we'll check to see if the element name is going to be equal to channel. So if it's equal to channel then we know we're at parent level and we can pull out the title of that particular feed. Now, we're going to take a few liberties in this example, in this application. And that, we're gonna assume that we're only gonna be working with one of these particular feeds at a time, and we're only gonna be downloading and parsing one at a time. So, in that case, we can kind of take a few liberties to say we're gonna create a few properties at the class level so that we can know and store data and do this kind of piecemeal. Now, if you were gonna do multiple parsers at a time and download multiple feeds at a time, this would run into a problem, you're gonna have to make a few changes to allow that sort of functionality to happen. So having said that, I'm gonna create a couple properties up here that are gonna be used to piecemeal these feed in article model classes or model objects together. So, we're gonna start with a feedTitle. So, we're gonna need to know what the feed title is, which is gonna come from the channel. So, this is gonna be a new instance of a string. We're also going to want to know the feedUrl. Now typically, this is actually gonna get passed into this. So you're not gonna get the URL, really, out of the channel. You can, and there's ways that you could probably do that, in some instances, but it's not always consistent across all the different ty, types. So the feedTitle is really all that we're gonna get out of the channel when we're parsing it. Then there's a few that we're gonna wanna get out of the item or the article level of the feed so we're going to have an article title that's gonna be a string. I'll initialize it to a new instance of a string. We're also going to have a, a link that we're gonna get out of that particular item, so we're gonna have an articleLink. This is also going to be a string. We'll initialize it to a string. We're also going to have a pub date, so we'll call this the articlePubDate. Once again, this is going to be a string, set that to a string as well. And that should just about do it and as I'm thinking about this now too, we're also gonna need to know while we're parsing this guy, what we're actually parsing. Are we gonna, are we currently parsing a channel, or are we currently parsing an item. So we're gonna need to have a little flag here. We'll just call this a, we'll call this parsingChannel and we're gonna make this a Boolean that will originally set it to false. So whenever we are actually parsing a particular channel, we're gonna set this to true so that we know that, that's happening and if it is for parsing an item, we will set that back to false. And then finally, we're gonna create one other little placeholder and we're just gonna call this eName. This is gonna be a string as well. And this is gonna help us to know which element we're currently parsing. So now we're gonna come down and modify our implementation here just a little bit. So when we get into this didStartElement from our parser, we're actually going to save this element name into our eName property, so we can know which one we're currently parsing at the moment. So if we're currently parsing a channel then we're gonna want to make sure that we reinitialize our feedTitle back to an empty string. Cause at this point, we're, we're parsing a new channel and we're going to set our feedUrl, we'll just kinda empty that out as well. And we'll also set our parsingChannel little flag here to be equal to true. Cause at this point, we know that we're gonna be parsing out a channel. Now we're also gonna need to know if our element name also gonna need to know if our elementName is equal to item. Cause as I said, within the world of RSS, the item is the article level, so that's where all of that stuff is gonna come into play. So at this point, we're gonna want to make sure that we reinitialize all of our article properties here. So we'll say our articleTitle is gonna be equal to a new instance of a string, our articleLink is gonna be equal to a new instance of a string. Our articlePubDate is going to be equal to a new instance of a string, and we're also gonna say that as far as parsing a channel is concerned, we are not parsing a channel at this point. So that's gonna be set to false. So really at the point when we start parsing a new element where we need to know if it's a channel or if it's an item to represent either a feed or an article, and then we're going to reinitialize the appropriate properties so that we can go forward and start doing the actual processing itself. Now the actual processing itself is gonna take place mostly in this foundCharacters. This is where we're gonna start getting actual data associated with these different levels, the feeds or articles. So now when we start getting into here, we need to do one little thing. We're gonna create a constant here because it's gonna be always constant from within the perspective of this particular function. We're gonna call this data, and this is gonna be equal to, and we're gonna have a little bit of magic here. So we're gonna use a method on the string that's coming in here, this foundCharacters string right here. We want to kind of play with this a little bit and make sure that we're cleaning it up. So we're actually gonna call a [UNKNOWN] method here called stringByTrimmingCharactersInSet. And as you can see, this returns a new string made by removing from both ends of the string characters contained within a given character set. So, what we wanna do is we want to trim a bunch of the unnecessary white space and all that junk off of there so we don't have to worry about doing it manually. So, this is a nice little way to do that for us. And we can use the NSCharacterSet helper here that's going to allow us to say within there we don't want, we wanna make sure we trim out all the white space and new line character set. Cuz there can be a lot of that garbage in there which could cause problems when we're actually starting to work with some of this data. So now we have this data, and from within here, we are going to actually start to pull out the different pieces of data, whether that would be the feedTitle, or the articleTitle, link and PubDate, depending on where we are. So once again, we're gonna have to check. We're gonna say, well first of all, we wanna know if this is actually empty. So if there's nothing here, then we just want to continue going down our [UNKNOWN] path. We don't wanna do anything. But if it's not empty, we need to know if we are currently parsing a channel. Because if we're parsing a channel, all we really are gonna get out of here really is the feedTitle. So if we're parsing a channel. And then finally, we need to check whether the actual element we're, we're taking a look at within this particular element is title. Because that is the, that is the element that's gonna contain our title that we're gonna associate with our feed. So once again, we're gonna nest this a little bit deeper, one last time, and say, if that eName that we set in here didStartElement, is going to be equal to title. Then we know this is the actual piece of data we want, so we're gonna set our feedTitle property equal to, or actually we'll just append onto this data. So there you have it. So, we have had a go down a couple, a couple levels and a little bit of nesting going on here. That's so, like I said, it's not the prettiest. You can definitely play around with this to make it a little bit more your style but this is gonna get the job done. So if there's data available to us, if we're currently parsing a channel and if that particular elementName is title then we want that because that is our feedTitle. Now we're going to come back out and say, if we're not currently parsing a channel, that means we're in the actual item that's gonna be associated with an article. We can now come in here and take a look at, we could say, well all right, if the eName, at this point, is equal to title, but this means we're at the article level now. We now, we would say that the article title is actually gonna be that data. Or we could say else if that name is equal to link, none are article link is going to be appended on that, appending on that data. And then finally, else if eName is gonna be equal to pubDate then we can set our articlePubDate equal to that data, like so. And that's gonna finish up our foundCharacters function here.. And then finally, we need to know when we've finished actually looking at a particular element and then we can start to clean things up and actually create a feed and article objects. So we need to know what we are finished parsing, so in this case, we once again have this elementName in the, in the parameter list. So we can say, if the elementName is equal to channel, which means we have finished parsing everything, we're all done, or the else statement here is going to be, if we have finished parsing a, an item, which is gonna represent that article. So if we've finished parsing a channel, we can go ahead and create a new feed. So we'll say var feed, actually we'll make this a, a constant, cause in this case, it's just gonna be within the context of this function it's always gonna be the same. So we'll say that we're gonna create a new instance of our FeedModel. And we don't, we haven't specified any custom initializers, so we're just going to initialize it like this. [SOUND] We could say our feed.title is gonna be equal to feed title, the property that we've been playing around with here. That our feedUrl is actually going to be what we passed in or what we set this up to be in the beginning, our feedUrl. And then our feedArticles are going to be equal to something yet, but we haven't actually got that far, so we're gonna fill this in just a second. [SOUND] So we'll just comment this out for a moment, feed.articles. So as we've finish parsing out the items here, I was a little shortsighted in leaving this as just an else. Because we could run into several different elements in here that we don't really care about. So we're gonna change this to an else if elementName is equal to item. And then we need to actually start to parse out these particular items into our article. So we'll say, let article, and we're gonna set that to be an ArticleModel. And we're gonna set that equal to the default initalizer of an ArticleModel. And then we'll just start to fill in the data. So we'll say that the article.title is gonna be equal to our article title that we've been populating to this point. And the article link, and the article.link is gonna be equal to our articleLink. And finally, our article.pubDate is gonna be equal to our articlePubDate. So that's pretty nice. We finished up this particular article, but what are we gonna do with it and how are we gonna get it back into the channel, or into the feed. Well, we're gonna need a couple more properties up here. So we're gonna create a feeds collection, a feeds array, if you will, that's going to allow us to save or hold onto a bunch of these feeds for later on. So this is actually going to be a collection of, or an array of feed models, and we'll just initialize that to an empty array. And we'll do the same with articles. We'll say articles, [SOUND] and this will be an array of ArticleModel, and we'll initialize that to be an empty array once again. So now, when we're done parsing out, we can actually take this particular article. We can go into articles, which is an array. And we can append to it our new article. [SOUND] Like that. And then, once we've gotten to this point, we've finished doing a channel, we can do two things. We can say that our feed articles is actually going to be equal to the articles that we've been accumulating to this point. And then we can add to our feeds collection or append to it, our feed. And that is the basic strategy that we're gonna use to actually parse the state out and I know it's not the prettiest, but let's just walk through really quick one more time. So what we did is we created a number of different placeholders that we're gonna use to piecemeal these feed and article objects together. We have implemented three delegate functions of our NSXMLParser. We have didStartElement, foundCharacters and didEndElement. Within the start element, we're checking to see whether or not we're parsing a channel which will represent our feed, or an item which will represent our article. And then we're going to initialize or reinitialize the different properties that we're storing accordingly for each one of those different pieces of data. When we actually start parsing out the data in the foundCharacters function, we're gonna do either grabbing the title out of a channel or the title, link and pub date out of an article. So once we've done that, we now have these different properties that we've created up above, populated with some data from our RSS feed. And then finally, when we end parsing an element, we're gonna put all those pieces of data into either a feed if it's a, into a feed model if it's a channel, or into an article model if it's an item. And it will put them in their respective collections so that we can start to deal with them later. So that is now the basics of parsing out all of this data. Once we have all this data, what the heck do we do with it? Well, we present it to the user through the view using the view controller. So we're gonna do that in the next lesson.