Nitish Nitish - 5 months ago 18
iOS Question

Common API request handler

One of the request in my app looks like this :

-(void)login
{
@try {
NSString *str = [NSString stringWithFormat:TGURL_LOGIN];
NSURL *url = [NSURL URLWithString:str];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSDictionary *requestData = @{@"---": ----,
@"---": ----,
@"OutResponse": [NSNumber numberWithInteger:0]};

NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject: requestData options:0 error:&error];

NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];


NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPBody:postData];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
__block int iSuccess = 0;

[NSURLConnection
sendAsynchronousRequest:request
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if ([data length] >0 && error == nil){
dispatch_async(dispatch_get_main_queue(), ^{
iSuccess = [self parseResponse:data];
});
dispatch_async(dispatch_get_main_queue(), ^{
if(iSuccess == 1)
{
[[NSUserDefaults standardUserDefaults] setObject:_email.text forKey:@"EmailText"];
[[NSUserDefaults standardUserDefaults] setObject:_password.text forKey:@"PasswordText"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"User_Logged_In"];
[[NSUserDefaults standardUserDefaults]synchronize];

[TGProjectHandler saveCookiesToDefaults];
[self getUserID];
}
else if(iSuccess == 3)
{
[[NSUserDefaults standardUserDefaults] setObject:_email.text forKey:@"EmailText"];
[[NSUserDefaults standardUserDefaults] setObject:_password.text forKey:@"PasswordText"];
[[NSUserDefaults standardUserDefaults]synchronize];
[self.view endEditing:YES];
[TGProjectHandler saveCookiesToDefaults];
[TGProjectHandler removeLoadingIndicator];
[self performSegueWithIdentifier:@"ShowJoinGroup" sender:nil];
}
else
{
[[[UIAlertView alloc] initWithTitle: NSLocalizedString(@"Login_failed", nil)
message:NSLocalizedString(@"Invalid_credentials", nil)
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
[TGProjectHandler removeLoadingIndicator];
self.view.userInteractionEnabled = YES;
}
});
}
else if ([data length] == 0 && error == nil){
NSLog(@"Empty Response, not sure why?");
}
else if (error != nil){
NSLog(@"%@", error.description);
self.view.userInteractionEnabled = YES;
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Login_failed", nil)
message:error.localizedDescription
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
[TGProjectHandler removeLoadingIndicator];
self.view.userInteractionEnabled = YES;
}
}];
}
@catch (NSException *exception) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}


This is just one of the service call. Have hundred's of them. Currently I am writing these in the controller itself (Bad approach, I know :)).

I am looking for a way where I can write all the request based code in a single class and access it everywhere.

For a moment I thought about creating Singleton. But I am not sure about creating Singleton for this.

Also thought about creating a protocol, but then finally I have to implement it in the controller.

What are the different patterns I can go for, so that I have a common class for the code I am repeating everywhere.

Answer

I have done like this, I have made a utility class

this is my webservice.h file

#import <Foundation/Foundation.h>

typedef void (^webCompletionHandler)(NSData *data);
typedef void (^webFailuerHandler)(NSError *error);


@interface WebService : NSObject



-(BOOL)callPostWebService:(NSString *)methodURL
                    param:(NSMutableDictionary *)params
        completionHandler:(webCompletionHandler)completion
          failuerHandler:(webFailuerHandler)failuer;


-(BOOL)callgetWebService:(NSString *)methodURL
        completionHandler:(webCompletionHandler)completion
          failuerHandler:(webFailuerHandler)failuer;



@end

Here i have created two handlers one for completion and another for failure

and these are the two methods for Post and Get

-(void)getWebServiceCall:(NSString *)methodURL
              parameters:(NSMutableDictionary *)parameters
       completionHandler:(webCompletionHandler)completion
          faliureHandler:(webFailureHandler)failure;

-(void)postWebserviceCall:(NSString *)methodURL
                    param:(NSMutableDictionary *)params
        completionHandler:(webCompletionHandler)completion
           faliureHandler:(webFailureHandler)failure;

This is the post method i have used

-(void)callPostWebService:(NSString *)methodURL
                    param:(NSMutableDictionary *)params
        completionHandler:(webCompletionHandler)completion
          failuerHandler:(webFailuerHandler)failuer{

    if(![Utility isReachable]){
        DisplayLocalizedAlert(@"Network is not reachable");
        return;
    }


    NSLog(@"%@",methodURL);
    NSURLRequest *request = [self createRequest:params url:methodURL];

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask =
    [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {


        dispatch_async(dispatch_get_main_queue(), ^{
            if(error != nil){
                failuer(error);
                return ;
            }
            else{

                NSError* error;
                NSDictionary* json = [NSJSONSerialization
                                      JSONObjectWithData:data
                                      options:kNilOptions 
                                      error:&error];
                NSLog(@"json %@",json);

                completion(data);
            }
        });
    }];
    [dataTask resume];

    return true;
}

Then where ever you need to call the webservice you can just call the post and gte method, whichever you require by passing the url and the parameters

[webService postWebserviceCall:strURL param:dicInfo completionHandler:^(id data) {

    } faliureHandler:^(NSError *error) {

    }];

In Swift:

import UIKit
import MobileCoreServices
import CoreLocation


typealias webCompletionHandler = (data : NSData) -> Void;
typealias webFailuerHandler = (error : NSError,isCustomError : Bool) -> Void;


let failureStatusCode = 0;
let successStatusCode = 200;

class WebServiceCall :NSObject {

    static var webService = WebServiceCall();

    override init() {

    }

func callPostWebService(methodURL methodURL :String, param:NSDictionary, completionHandler:webCompletionHandler, failureHandler: webFailuerHandler) -> Bool
    {
        if (isInternetHasConnectivity() == false ) {
            let myError = NSError(domain: "Internet is not available", code: 1001, userInfo: nil)
            failureHandler(error: myError, isCustomError: true);
//            AlertView.showMessageAlert(myError.domain)
            return false;
        }

        let request = self.createRequest(param, strURL: methodURL)

        let serviceTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {
            data, response, error in

            dispatch_async(dispatch_get_main_queue(), { () -> Void in

                if(error != nil){

                    failureHandler(error: error!, isCustomError: false);
                }
                else{

                    let response = Utilities.parseData(data!)
                    let status = response.objectForKey("status") as! Int;

                    if(status == successStatusCode){
                        completionHandler(data: data!)
                    }
                    else {
                        let msg = response.objectForKey("message") as! String
                        let error = NSError(domain: msg, code: Int(status), userInfo: nil);
                        failureHandler(error: error, isCustomError: true);
                    }

                }

            })
        })
        serviceTask.resume();

        return true;
    }
}