A lightweight HTTP request class for iOS [2/2]
Part 1 | Part 2
Check out SMWebRequest on Github.
In Part 1, we introduced
SMWebRequest
for making HTTP requests with little
code. For instance, here’s how your view controller could
download an RSS feed and parse it with
SMXMLDocument:
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
SMWebRequest
in -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 JobsSo 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 SMWebRequest
.
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.