Making Network Calls in iOS

A very common task of any developer is to write a web services class. In this post I will present a very common pattern for organizing and incorporating network calls into iOS apps.

To give a quick overview, we will be creating a singleton web services class. This basically means we will make a new class that stores all the methods for making network requests, and that the same allocation of this class in memory will be accessible across all views in the app. If you want to learn more about this design pattern, look at my other post.

So, lets create this class. In Xcode, create a new objective c file that subclasses from NSObject.

The header file will look like this:


// WebServices.h
// DC_magazine
//
// Created by Andrew Rauh on 6/4/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface WebServices : NSObject

@property (strong, nonatomic) NSMutableArray *blogPosts;
@property (strong, nonatomic) NSMutableArray *titles;
@property (strong, nonatomic) NSMutableDictionary *selectedPost;

- (void) grabDataAndParse;
- (void) buildTitleArray;
+(id) sharedInstance;

And here is the implementation file. (.m)

</pre>
//
// WebServices.m
// DC_magazine
//
// Created by Andrew Rauh on 6/4/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "WebServices.h"

@implementation WebServices
@synthesize blogPosts, titles,selectedPost, ;

+(id)sharedInstance
{
 static id sharedInstance = nil;
 if (sharedInstance == nil) {
 sharedInstance = [[self alloc] init];
 }
 return sharedInstance;
}

-(void)grabDataAndParse
{
 selectedPost = [[NSMutableDictionary alloc]init];
 NSURLRequest *main = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://dcfaithinaction.org/?json=get_recent_posts"]]];

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 dispatch_async(queue, ^{
 NSURLResponse *response = nil;
 NSError *error = nil;

 NSData *data = [NSURLConnection sendSynchronousRequest:main
 returningResponse:&response
 error:&error];
 NSString *json = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
 id parsedObject = nil;
 NSError *parseError = nil;
 if (json != nil && [json isKindOfClass:[NSString class]] && [json length] > 0) {
 parsedObject = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSASCIIStringEncoding]
 options:0
 error:&parseError];
 }
 if ([parsedObject isKindOfClass:[NSDictionary class]]) {
 NSDictionary *parsedDictionary = (NSDictionary *)parsedObject;

 NSArray *posts = [parsedDictionary objectForKey:@"posts"];
 blogPosts =[NSMutableArray arrayWithArray:posts];
 [self buildTitleArray];
 }
 });
}
- (void)buildTitleArray
{
 titles = [[NSMutableArray alloc] init ];

 for (NSDictionary *dict in blogPosts) {
 [titles addObject:[dict objectForKey:@"title"]];
 //NSLog(@"%@", [dict objectForKey:@"title"]);
 }
}

@end
<pre>

In this case, I used NSURLConnection with an asynchronous block to make the actual network call. After you make the call, you get NSData back. You then treat this as a generic objective-c object, and see if it is either a dictionary or an array. Once you figure out what data type the object you got back from the server is, cast it to that type of object and start iterating over its elements, and extract the needed data.

The reason this network call must be made asynchronously is pretty simple. On iOS, there is a main thread, where all of the UI code is run. If we put this networking code on that same thread, then all of the UI on the devices would completely freeze until we got the needed data back from the server.

2 thoughts on “Making Network Calls in iOS

  1. Hollar! Few things to add:

    * Don’t forget to get back on the main thread for sake of best practice, unless of course there’s a good reason to stay off-thread. You probably want the following after you’ve received all your data:
    dispatch_async(dispatch_get_main_queue(), ^{
    /* Handle response */
    });

    * Along with “error”, worth checking HTTP status code for non-200’s as well. To my knowledge error is only populated if there is a failure at the socket/TCP level, not HTTP. For those playing the multi-language game, C# and Obj-C differ in this way… .NET exceptions for all of the above. Note – testing data for null (or len 0) is a bad way to check for errors, as you can get back 404’s/500’s/etc and still have data sent from server.
    NSHTTPURLResponse *httpResponse;
    if([genericResponse isKindOfClass:[NSHTTPURLResponse class]])
    httpResponse = (NSHTTPURLResponse*) genericResponse;
    if(error || (httpResponse.statusCode != 200))
    {
    NSLog(@”Connection failed %@ (Error %@, HTTP Error %i, Custom Server Error Msg %@)”, request.URL.absoluteString, error, (int) httpResponse.statusCode, [httpResponse.allHeaderFields objectForKey:@”CustomServerErrorMsg”]);
    }
    CustomServerErrorMsg above represents the custom response header optionally set by your server for extended error info.

    * Can use [NSURLConnection sendAsynchronousRequest] to skip the dispatch_async steps, though if you’re making multiple requests in a row you definitely want to use the method above to streamline. For single web calls though:
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *genericResponse, NSData *data, NSError *error) {
    /*Handle response*/
    }];
    Using this method also brings you back to the main thread for handling the response. (Alternatively, use currentQueue instead of mainQueue to just return to the current thread.)

  2. You mention that the reason you use an asynchronous network call is to prevent the main thread from freezing the ui elements. Isn’t it the use of gcd that prevents this, not the asynchronous network call?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s