How to consume JSON or XML Web APIs on iPhone smoothly
By Mark on October 15th, 2010 in TechnobabbleTags: design, how-to, iPhone, web api
I answered this post on StackOverflow about asynchronous web requests, and having recent experience with the topic fresh in my head, here is a more detailed approach.
Often times in iOS, you need to retrieve data from a Web Service (or Web API) and display it to the user in some format. Â My recent project consumes an RSS feed (XML result) and runs a catalog search (JSON result) when the user does certain things inside the application. Here is the approach I used to keep everything clean and simple.
First, the major concern is that you cannot have a synchronous Web request on iOS devices – if you use a synchronous request, it will lock the user interface, and users will think your app has frozen.
On the other hand, when you use an asynchronous request, how will you “know” when your request is ready and finished, or worse yet, what if it fails? Apple loves the delegate pattern, and this is another implementation: NSURLConnection uses a delegate callback to let you know when an asynchronous request is done.
However, the temptation here may be to initiate your request inside of your view controller, then then to assign your view controller as the delegate of the NSURLConnection. I recommend against this; I highly recommend you create an “API class” whose sole responsibility is processing and handling all of the transport layer (HTTP) and text processing (JSON/XML) work. UIViewControllers should be used for controlling views.
Let’s get started.
Setting Up the API Call in the View Controller
I am going to call our API class WebServiceAPI, and when a certain view controller loads, we want our API to fetch some data. Our view controller’s viewDidLoad might look like this:
- (void) viewDidLoad
{
[super viewDidLoad];
WebServiceAPI *api = [[WebServiceAPI alloc] init];
[api retrieveRequestWithSomeData:@"foo" delegate:self];
// webApi would be a retained @property of my view controller, we want to hold on to it
self.webApi = api;
[api release];
}
Look at the [api retrieveRequestWithSomeData:@"foo" delegate:self] line – I’ve specified my view controller class as the API’s delegate. What methods must we implement to make this work? Well, when I send an API request, I want to know when it finished successfully, and I want to know if it failed (network connection problems, no authentication, etc.). So, we’ll define a protocol in the API header file that the view controller must implement.
// In the WebServiceAPI.h header file @protocol WebServiceAPIDelegate @required - (void) apiFinished:(WebServiceAPI*)api; - (void) api:(WebServiceAPI*)api failedWithError:(NSError*)error; @end
We also need to make sure the API has a delegate property so that it can “remember” it is supposed to call back to the view controller when it is done:
@property (nonatomic, assign) id <WebServiceAPIDelegate>delegate;
The angle brackets tell the compiler than anything that is assigned to this property must conform to the WebServiceAPIDelegate protocol that we’ve defined above – so you’ll get a nice warning from the compiler if you fail to implement any of the methods.
Note that when you create your own delegate property for a class, you probably want to use assign, not retain, to avoid a memory-wasting retain cycle (you can read more about this in Apple’s memory management docs).
Writing the API Abstraction Layer
First, the retrieveRequestWithSomeData:delegate: method that the view controller is calling:
- (void) retrieveRequestWithSomeData:(NSString*) foo delegate:(id) aDelegate];
{
// Set the WebServiceAPI delegate to the view controller
self.delegate = aDelegate;
/** Some code to process "foo" would go here and create an NSURLRequest **/
NSURLConnection *aConnection = [NSURLConnection connectionWithRequest:req delegate:self];
// Hold on to our connection in case we need to cancel him
self.connection = aConnection;
}
It’s important to notice that our call [NSURLConnection connectionWithRequest:req delegate:self] looks a lot like the [api retrieveRequestWithSomeData:@"foo" delegate:self] call from the view controller.
We are creating an abstraction layer – we do not want our view controller to have to deal with the implementation details of how the API gets its data (in this case, NSURLConnection) – simply that it does. This promotes code reuse (you could use the same view controller for a different API, for instance), and if you need to make changes to how the API works, you don’t need to mess with your view controller code.
So, at this point, we’ve sent our Web request to NSURLConnection, set the delegate of NSURLConnection to an instance of WebServiceAPI, and set that instance of WebServiceAPI’s delegate to the view controller. Now let’s get to the response.
Implementing NSURLConnection’s Delegate Methods
After a successful request, NSURLConnection will callback to WebServiceAPI, it’s delegate, so we should implement the delegate method:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection
{
// Do any processing with the returned data - parse JSON/XML etc
// Then store it in an instance variable of the API
// Tell our view controller we are done, pass "self" so clients can access the retrieved data.
[self.delegate apiFinished:self];
}
To stay consistent with Cocoa Touch’s approach to delegate callbacks, we don’t pass the response data directly back to the API delegate – instead, we say “I am finished now, here I am”, and pass the API object itself. Then, a client (in our case, the view controller) should access the information it is interested in.
I haven’t included it in the sample code here, but you’ll need to make a retained @property of a dictionary, array, string, or anything else that you want your clients to be able to finally “access” from the API.
Handling NSURLConnection Failures in the API
Especially on iOS devices, we can never assume “it’s just going to work” – we need to handle all failure cases smoothly.
- (void) connection:(NSURLConnection*) connection didFailWithError:(NSError*) error
{
// Custom logic goes here before sending the error on, sending on a different error, etc.
[self.delegate api:self failedWithError:error];
}
In this sample, there is no implementation – we just pass the error back to the view controller as a simple passthrough. However, this is really only useful for the most basic of implementations. Usually, this method is where the power of the abstraction makes itself clear.
For example, if our user cancels the request, we will cancel the NSURLConnection as well. When the connection has successfully cancelled, NSURLConnection will report an error – but that error is a “I could not complete because I was cancelled” error. Chances are, if our user cancelled the request on purpose, we don’t want to report an error back to the view controller – it was probably the one who told the WebServiceAPI to cancel the request in the first place.
Another possible case could be that the network died in the middle of the connection, but you absolutely “need” the data – so we change the API to pull out a cached copy from Core Data and tell the view controller we were successful anyway. The view controller would never even know there was a failure at all – the API could be smart enough to handle the network availability issue.
Implementing the WebServiceAPIDelegate Methods
As a final step, we have to implement the WebServiceAPIDelegate methods in the view controller. I leave this as a task for the user, because the implementation varies greatly on what you are trying to achieve. In the project code I worked on, the view controller was a UITableViewController, so after the API returned success, I set the response data array (a property of the API) to a property of the view controller, and then called [self.tableView reloadData] to tell my UITableView to update the user interface.
In the failure case, I show a UIAlertView telling the user the nature of the error – no network, etc.
I hope this tutorial has been helpful and/or instructional. If you have any ideas on how to make it better, please feel free to let me know in the comments.





12 Responses to “How to consume JSON or XML Web APIs on iPhone smoothly”
Michael Robinson says:
I am trying this, – (void) retrieveRequestWithSomeData:(NSString*) foo delegate:(id) aDelegate; {
On this line: self.delegate = aDelegate;
I get “not in a structure or union” error, Ideas?
do you have a code sample? thx.
Oct 27, 2010 | Reply
Mark says:
Michael,
As I saw in the post on StackOverflow, it looks like you’ve got this sorted by making sure your code had this:
@property (nonatomic, assign) id delegate;
Oct 27, 2010 | Reply
Michael Robinson says:
Thanks, I want to load this again after I add a record to the database, but I’m not sure where to how to implement, I moved the API call to a method and am having viewDidLoad call it. After I add a record I was having the AddRecord.m file call the API method to run the DelegateMethod again. What is annoying be now is that I seem to be in a loop that contatntly wants to display the first row of my api.data. Any Ideas on how to call this but only have it reload once, not every time I select a row? Thanks for getting me this far.
Oct 28, 2010 | Reply
Mark says:
Michael,
I’m not exactly sure what you mean – adding a record to the DB. Does this mean that after getting the data from the API, you store in locally in Core Data or some sort of database?
Then, you add a record locally? Or you want to “refresh” and have the API get the latest record? Not sure what you’re trying to do.
Oct 29, 2010 | Reply
Ken says:
Would you consider releasing a downloadable zip of this working in its basic form? I’m having a difficult time implementing this in my app.
Dec 9, 2010 | Reply
Mark says:
I could do that. We’re knee-deep in working on a new app right now on a deadline, so I can’t put it together right away for you.
If you e-mail me privately with code and/or what parts you’re having problems with, I’d be happy to take a look and help.
Sorry I can’t be more useful! Next time I do a tutorial I’ll have to include a sample project.
Dec 9, 2010 | Reply
Simon Hopkins says:
Many thanks – this was really useful. My code is now far better structured and robust!
Mar 25, 2011 | Reply
iphone says:
thanks alot.
ths is very nice tutorial & very useful for newbie..
Jul 17, 2011 | Reply
pavan says:
Hi Mark,
The information you have provided is very useful. It will be more helpful if you can share a sample project/tutorial on this.
Aug 17, 2011 | Reply
Desparate Novice says:
You have wasted your time with a post not that helpful to others. This post does not help to any novice reader nor advanced reader will read “Where to use delegate” topics.
You should know who your audience are.
I have also wasted my time learning from this post.
May 4, 2012 | Reply
Mark says:
@Desperate Novice,
You’re the best troll I’ve ever encountered.
Forgive me for my unhelpful and utterly free content on the Internet! You get what you pay for!
May 4, 2012 | Reply
Karthik says:
I was looking for search bar example with source code as well as json parsing example with source code.I have been searching for a while but i couldn’t find out.could you please send me thro mail
Oct 9, 2012 | Reply