A lightweight HTTP request class for iOS [2/2]
Part 1 | Part 2
Looks simple, but there’s a catch. See how we’re taking our
NSData response from the server and turning it into XML? Well that’s happening on the main thread, and guess what, parsing XML or JSON or whatever takes time. Maybe it takes a few milliseconds, or maybe it takes 10 whole seconds like when we parsed a gnarly RSS feed on the original iPhone.
And your poor user, well she’s probably scrolling through a table view or web view or something and just having a great time and then screech everything grinds to a halt while you parse your giant 8MB SOAP response.
The proper thing to do here is parse your data on a background thread. Which is relatively simple to do in Cocoa:
OK, so not terrible, but we’re starting to have a lot of code and callbacks in our controller, and now we have to start worrying: What if the user leaves this page while we’re in the middle of doing this work? We can easily cancel the
-dealloc since it doesn’t retain us as a target. But we can’t kill the background thread if it’s running and the system will
-retain us anyway so we have to wait until it completes and then detect somehow that we’re no longer wanted so we don’t kick off new operations. And what if the user presses “Refresh” or picks a different RSS feed or something while we’re working in the background? Now we have to detach new background threads and detect old ones finishing and it gets really hairy.
“Who wants a stylus?! You have to get ‘em, and put ‘em away, and you lose ‘em. Yeeech. Nobody wants a stylus.”—Steve Jobs
So let’s not use a stylus. Basically, we don’t want our controller class to have to worry about any of this stuff. The process should be abstracted away as “Ask for RSS feed, receive RSS items later.”
And that’s exactly what
SMWebRequest is designed for. In addition to the target/action callbacks,
SMWebRequest supports the
delegate pattern for processing data separately:
There are a couple great things happening here. First, we implement the delegate’s
-webRequest:resultObjectForData:context: which is a totally isolated method. Its only job is to turn
NSData into objects. In this example, it’s a
Class method which is great for background threads because it can’t accidentally touch our instance variables even if it wanted to.
Second, the controller’s “complete” method automatically receives the
NSArray we care about, rather than the whole
NSData. Ask for RSS feed, receive RSS items. And the whole operation is automatically cancelable just like any
SMWebRequest because it manages the threading for you.
We can make our controller even cleaner by moving the parsing out into a model class:
And this is how we write all our apps.
The github project demonstrates all this nicely by building basically an entire Hacker News RSS reader using very little code, whipped up by Benjamin van der Veen, the original author of
I actually installed it on my iPhone and use it now to waste even more time than I already waste reading stuff on the internet.