Search code examples
iosobjective-cafnetworkingafnetworking-2

AFNetworking 2.0 responseObject handling


I have a question. Maybe 2 questions.

First one relates to this piece of code:

-(void)isLoggedIn {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

I would like that this method is not a void, i would like it to be returning a BOOL or something that I can use like: User is logged in > Ok do something somewhere in the app; User is not logged in > Ok prompt the user to a screen where he can introduce his credentials and log in.

Second question is related to this piece of code:

-(void)detail {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUser];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

I would like that this method returns me a NSArray with the parsed JSON, which i can use wherever i want to.

This methods are contained into a Model called NetworkModel (NSObject subclass). I'm just wondering why i should do everything inside the blocks. I should have pieces interacting with network all over the place, and not organized in one unique place/class. Is there a way to do that. I don't want to have 5 lines of requests every ViewController. I want to define methods i can call from any ViewController (obviously importing the header files where needed).

Any suggestions? Thank you in advice!


Solution

  • You can do this with 2 approaches, one change your methods in order to use blocks or use a semaphore to wait for the async request:

    With semaphore (THE UGLY WAY)

    -(BOOL)isLoggedIn {
    
    __block BOOL isLoggedIn;
     dispatch_semaphore_t waiter = dispatch_semaphore_create(0);
    
    NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
    [manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"JSON: %@", responseObject);
        isLoggedIn = YES;
        dispatch_semaphore_signal(waiter);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
        isLoggedIn = NO;
        dispatch_semaphore_signal(waiter);
    
    }];
    
    dispatch_group_wait(waiter, DISPATCH_TIME_FOREVER);
    return isLoggedIn;
    }
    

    The Right Way to do it

    -(void)isLoggedIn:(void(^)(BOOL isLoggedIn))completionBlock {
    NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
    [manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"JSON: %@", responseObject);
        completionBlock(YES);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
        completionBlock(NO);
    }];
    }
    

    For your second question, you can set a block variable on your networking class that will be called when the request is completed or it fails.

    Anyway I think you are not quite familiar with blocks (because of your second question) and I think after a few blocks you will start to like them and you will see that the code is not complicated if you have small snippets of code that perform request & parsing, also you will see that it's soooo easy to debug them and find the problems, I mean, this was also my case when I started to work with blocks.

    Using a block variable

    @interface YourNetworkClass:NSObject {
    void(^resultHandlerBlock)(NSArray *results); //this block has as param a NSArray, you can change it to whatever you want
    }
    
    -(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock;
    
    @implementation YourNetworkClass
    
    -(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock {
    resultHandlerBlock = block;
    }
    
    -(void)anotherMethodThatDoesNetworkOperationsAndReturnsAnArray {
     ....
     resultHandlerBlock(arrayOfResults);
    }
    
    
    @interface AViewControllerThatUseNetwork: UIViewController {
    }
    
    @implementation AViewControllerThatUseNetwork 
    
    -(void)initNetworkClass {
      YourNetworkClass *networkClass = [[YourNetworkClass alloc] init]; //or other init methods
      [networkClass setResultHandlerBlock:^(NSArray *results) {
       //do whatever you want with the array
    }];
    }