Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing blocks to a AFNetworking method?

-(void )getDataFromServer: (NSMutableDictionary *)dict
 {

 NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/doSomething",MainURL ]];
 [AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:@"text/html"]];

 AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
 NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:nil parameters:dict];

 AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
 success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
 {
     _myArray = JSON;
     [_myTableView reloadData]; //Or do some other stuff that are related to the current `ViewController`
 }
 failure:^(NSURLRequest *request , NSURLResponse *response , NSError *error , id JSON)
 {
     NSLog(@"request: %@",request);
     NSLog(@"Failed: %@",[error localizedDescription]);
 }];

 [httpClient enqueueHTTPRequestOperation:operation];
 }

I'm using the above code in 7 different places in one of my apps. The exact chunk of code is duplicated in 7 of my ViewControllers. What I normally used to do is to put the method I want to use in a NSObject class and allocate and use it when ever I need but because the above is Async and using blocks I can't just return the JSON to the ViewController who called it and have to copy & paste the above method in every ViewController I need it in.

My goal is to have the above in only one place in my app and still be able to call it from different ViewControllers around my app and get the data I need. I'd like to avoid using an observer such as NSNotification or KVO and looking for a more elegant solution. After some reading I noticed the possibility to pass blocks around. Is that a possible solution with the above? A code example would be appreciated.

like image 271
Segev Avatar asked Aug 31 '13 15:08

Segev


1 Answers

Factorize the API call to something like

+ (void)getDataFromServerWithParameters:(NSMutableDictionary *)params completion:(void (^)(id JSON))completion failure:(void (^)(NSError * error))failure {
     NSString * path = @"resources/123";
     NSMutableURLRequest *request = [self.httpClient requestWithMethod:@"POST" path:path parameters:params];
     AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        if (completion)
            completion(JSON);
     } failure:^(NSURLRequest *request , NSURLResponse *response , NSError *error , id JSON) {
        if (failure)
            failure(error);
     }];

     [httpClient enqueueHTTPRequestOperation:operation];
 }

You can place this method in a utility class like XYAPI and just invoke it from your controllers like

 [XYAPI getDataFromServer:params completion:^(id JSON){
     // do something, for instance reload the table with a new data source
     _myArray = JSON;
     [_myTableView reloadData];
 } failure:^(NSError * error) {
    // do something
 }];

Also you don't need to spawn a new AFHTTPClient at every request. Configure and use a shared one in the XYAPI class. Something like

+ (AFHTTPClient *)httpClient {
    static AFHTTPClient * client = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://foo.com/api/v1/"]];
        [AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:@"text/html"]];
    });
    return client;
}

Note that this implementation is already used in the above example.
self in the context of a class method is the class itself, so self.httpClient is indeed resolved at runtime as [XYAPI httpClient].

like image 60
Gabriele Petronella Avatar answered Oct 04 '22 17:10

Gabriele Petronella