Manage HTTP Requests Through APIs In Your iOS App

 

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 5.00 out of 5)
Loading...

 

Pham Van Hoang, hoangk55cd@gmail.com, is the author of this article and he contributes to RobustTechHouse Blog

 

Most of applications today need to communicate with servers to exchange data. When an app needs to send data to the server, or fetch data from it, it makes the proper request for the purpose. That happens many times during the app runtime period. As an iOS developer will definitely run into it sooner or later. In this tutorial, I will introduce you:

  • AFNetworking: an iOS “must-have” library to deal with networking in iOS app.
  • How to design a APIManager class that easy to manage your HTTP requests (APIs) and reuse.

Let’s get started!

 

AFNetworking

AFNetworking is a delightful networking library for iOS and Mac OS X. It’s built on top of the Foundation URL Loading System, extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.

Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac” – AFNetworking page.

To use AFNetworking is pretty easy. For every request, you will create an AFHTTPRequestOperationManager or AFHTTPRequestOperation instance and send a GET/POST/PUT/DELETE request to handle the response. For example.

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//add authentication code
[manager GET:@"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
     	// 1. Parse response object
	// 2. Check and handle errors
        // 3.  Using data in your projects     
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
	// Check and handle errors
}];

 

It’s a simple and powerful library, huh!

However, you definitely will not want to rewrite the get/post/put/delete methods and the functions to check and handle errors in every request, since it will take time and be potentially hard to manage. Also whenever possible, try to avoid duplicating code.

 

Manage HTTP Requests (APIs)

To do that, we will need: a class that handle all requests; a class check to handle all errors; and a base class that handle common response data from server. In this example I created 3 classes: APIBaseObject, APIManager, APIErrorProcessor.

manage http ios

 

APIBaseObject

This is a base class that parse common response from server, every model object return from server will inherit from this class. You can see that in this class, we can handle the common response. For example: every response from server may have message key, and status key. You will handle in this base class instead of handling in every model object.

#import <Foundation/Foundation.h>

@interface APIBaseObject : NSObject
/*
 define common key here
*/

// parse response return from server
- (void)parseResponse:(NSDictionary*)response;
// parse common response

@end

 

APIManager

This is the class that handle and manage all requests.

In this .h file, we will have

  • requestOperationManager: an object that stores all requests and set maximum concurrent requests at time for our project.
  • A cancel current request function.
  • A custom GET/POST/PUT/DELETE request, we will make a request and handle all errors and data inside this function. It will return the response object as a result (in this example, I just demonstrate the POST request).
#import <Foundation/Foundation.h>
#import "APIBaseObject.h"
#import "APIErrorProcessor.h"
#import <AFHTTPRequestOperationManager.h>


// block that handle response return from API
typedef void(^ServerResponseHandler) (BOOL success, APIBaseObject *response);


@interface APIManager : NSObject

// has NSOperationQueue that handle all request

@property (nonatomic, strong) AFHTTPRequestOperationManager *requestOperationManager;


// singleton - public method
+ (APIManager *)sharedInstance;


/*
 @urlAPI: URL of API request
 @parameters: parameters send to server
 @responseObjectClass: Object Model that response from server – Sub class of APIBaseObject
 @responseHandler: (BOOL success, APIBaseObject *response) --> return status of request and object 
 @isBackgroundCall: check if it's background call so we may not show user message in this case

 */
- (void)sendPOSTRequestForAPI:(NSString*)urlAPI parameters:(NSDictionary*)parameters responseObjectClass:(Class)responseObjectClass responseHandler:(ServerResponseHandler)responseHandler isBackgroundCall: (BOOL) isBackgroundCall;


// Cancel all current request if they are requesting or in the queue
- (void)cancelAllRequest;


@end

 

Now, head over to .m file to implement the code that actually create and handle the request.

#import "APIManager.h"
#define WS_TIMEOUT      120

@implementation APIManager


+ (APIManager *)sharedInstance {
    static dispatch_once_t pred;
    static APIManager *shared = nil;
    
    dispatch_once(&pred, ^{
        shared = [[APIManager alloc] init];
    });
    return shared;
}

- (id)init {
    if (self = [super init]) {
        self.requestOperationManager = [AFHTTPRequestOperationManager manager];
        self.requestOperationManager.securityPolicy.allowInvalidCertificates = YES;
        // set max concurrent request, in this case I use 3. It's mean you can call upto 3 API at a time
        self.requestOperationManager.operationQueue.maxConcurrentOperationCount = 3;
    }
    
    return self;
}

- (APIBaseObject *)isBaseObject:(Class)c {
    id result = [c new];
    if ([result isKindOfClass:[APIBaseObject class]]) {
        return result;
    } else {
        return nil;
    }
}

- (void)cancelAllRequest; {
    // cancel all request 
    [self.requestOperationManager.operationQueue cancelAllOperations];
 }
- (void)sendPOSTRequestForAPI:(NSString*)urlAPI parameters:(NSDictionary*)parameters responseObjectClass:(Class)responseObjectClass responseHandler:(ServerResponseHandler)responseHandler isBackgroundCall: (BOOL) isBackgroundCall {
    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]requestWithMethod:@"POST" URLString: urlAPI parameters:parameters error:nil];
    [request setTimeoutInterval:WS_TIMEOUT];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    operation.responseSerializer = [AFJSONResponseSerializer serializer];
    
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        // check if input object is base object
        APIBaseObject *result = [self isBaseObject:responseObjectClass];
        
        if (result != nil) {
            // parse info to response object
            [result parseResponse:responseObject];
        }

        if (isBackgroundCall == NO) {
            // process common error
            [[APIErrorProcessor sharedInstance] process:result];
        }
        
        if (responseHandler) {
            responseHandler (YES, result);
        }
        
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // if request fails
        if (isBackgroundCall == NO) {
            [[APIErrorProcessor sharedInstance] process:error];
        }
        
        if (responseHandler) {
            responseHandler (NO, nil);
        }
    }];
    [self.requestOperationManager.operationQueue addOperation:operation];
}

 

Inside the request function, it calls APIErrorProcessor to handle the errors. Let’s take a look inside. This class will check the common network error and also the errors defined by developers.

#import "APIErrorProcessor.h"
#import "APIBaseObject.h"
#import "AFNetworkReachabilityManager.h"

@implementation APIErrorProcessor

+ (APIErrorProcessor*)sharedInstance {
    static APIErrorProcessor *processor = nil;
    static dispatch_once_t oncePredicate;
    
    dispatch_once(&oncePredicate, ^{
        processor = [APIErrorProcessor new];
    });
    
    return processor;
}

- (BOOL)process:(id)error {
    // check if it's network problems
    if ([error isKindOfClass:[NSError class]]) {
        [self networkCheckError];
        return YES;
        
    }
    // if it's request problems: wrong params, ect.
    else if ([error isKindOfClass:[APIBaseObject class]]) {
        // show message, define more error code here.
        
    }
    
    return NO;

}
- (void)networkCheckError {
    if (([AFNetworkReachabilityManager sharedManager].reachable == NO)) {
        // show error message
    }
}
@end

 

Pretty awesome, huh? Now you can create your API Manager for your own project, I will add the full code on my Github repository here.

Thanks for reading, if you have any questions, please leave your comments below. I will post my demonstration application using APIManager any time soon.

 

Brought to you by the RobustTechHouse team (Singapore based app development company).  If you like our articles, please also check out our Facebook page.

Recommended Posts
Contact Us

We look forward to your messages. Please drop us a note for any enquiries and we'll get back to you, asap.

Not readable? Change text. captcha txt
top food related apps singaporeThe Biggest Players In Artificial Intelligence Today