I've been asked a number of times to provide some examples on how to make use of AQXMLParser. A question which came in today has prompted me to make the effort and actually put together a simple project which demonstrates its use, along with its companion classes HTTPMessage and AQGzipInputStream.
The example project uses the asynchronous xml parser API to fetch my basic Tumblr post list, and displays the types and slugs of those posts in the UI (although it actually records all the data, in case some enterprising soul wants to actually make a detail view).
It fetches the data using a fairly simple set of code. I originally had it using a gzip stream, but it seems Tumblr never gzips its responses, so that didn't work out. I've left the appropriate code in there, albeit commented out, for your edification. In the snippets below, however, I've reinstated the gzip code.
The first thing to do is to create your URL and message:
If you want to receive gzipped data (and if the service obeys such commands) then
msg.useGzipEncoding = YES will configure the relevant HTTP request headers
The next step is to get a response stream from that message and potentially wrap it in a gzip stream if you've asked for gzipped data:
The parser itself is then built around the stream we just obtained. In this
instance we'll use a subclass,
AQXMLParserWithTimeout, which implements a timeout
or its own rather than waiting for the underlying protocol to decide it won't wait
any further. Note that the parser in this example is a member variable, since we're
using an asynchronous API and want to have a clean retain-release-autorelease
contract in place with the API.
Next up is the parser delegate, which we'll see in more detail shortly.
AQXMLParser is an event-based XML parser, so a delegate is required in order to
get anything out of the parsed data. Again, we're storing the parser delegate in a
member variable and releasing it when we know we're done with it.
If we were on a background thread already, we could run the parser in synchronous
mode by simply calling
[parser parse]. Since we're working asynchronously on a
single thread however, we will use a somewhat more wordy API:
At this point we sit back and wait for our callback to be called. Unlike in the example above, the callback can have three parameters: the two mentioned above (parser and success/failure state) and a context pointer. I'll add a blocks-based version shortly, I think.
When the parser finishes reading the response and handing the data on to the delegate, it will call the specified callback routine with the results. Here's the one from the sample application in its entirety:
Our next step is to investigate the delegate. This is a subclass of
AQXMLParserDelegate, which implements some nice magic for us. It takes the name
of an opening or closing XML tag and converts it into a message name, such as
endXXXX. For tag names which contain hyphens, it
removes the hyphen and camelCases the name, meaning that
endXmlTag. The delegate code then
checks whether it responds to the message name it's just generated, and if so it
will call it.
Additionally, the delegate accumulates the contents of tags as a string for you, so
endXXXX method you can access that text using
correctly handles CDATA blocks, so you don't have to worry about parsing those out
or trimming CDATA tags from the midst of anything.
Below are a couple of examples from the
TumblrParserDelegate class in the example
That's all there is to it! And of course you get all the benefits of a proper streaming parser, such as parsing data while it's being received on the wire rather than accumulating megabytes of XML data in memory before looking at it. Fast, low-overhead. Nice.
The full code can be found here.