Search code examples
objective-csynchronousafnetworking-2

How to use AFNetworking 2.0 synchronously


i have searched a lot of examples and tutorials on how to run AFNetworking 2.0 synchronously and found only solutions for AFNetworking 1.0. What i have found: Can AFNetworking return data synchronously (inside a block)?

My example:

- (User *)getUserWithUsername: (NSString *) username andPassword: (NSString *) password {

    NSDictionary *params = @{@"email": username, @"password": password};


    [[DCAPIClient sharedClient] POST:@"login" parameters:params success:^(NSURLSessionDataTask * __unused task, id JSONResult) {
        NSLog(@"JSON %@", JSONResult);
        BOOL errorCode = [JSONResult objectForKey:@"error"];
        if (!errorCode) {
            self.username = [JSONResult objectForKey:@"name"];            
            // Fill the attributes
            // self.email = .. a
         } else {
            // error with login show alert
         }

     } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
         NSLog(@"error %@", error);
     }];

     // this does not work
     //[[[DCAPIClient sharedClient] operationQueue] waitUntilAllOperationsAreFinished];


     if (self.username == nil) {
         return nil;
     }

     return self;

}

But this does not work, because if (self.username == nil) is called first.

How can i get AFNetworking 2.0 lib run synchronously that i can return response?

DCAPIClient : AFHTTPSessionManager

+ (instancetype)sharedClient {
    static DCAPIClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = [[DCAPIClient alloc] initWithBaseURL:[NSURL URLWithString:DCAPIBaseURLString]];
        _sharedClient.responseSerializer = [AFJSONResponseSerializer serializer];
    });

    return _sharedClient;
}

Solution

  • You should not make an inherently asynchronous method synchronous, but instead make your call-site asynchronous as well.

    That is, your method becomes asynchronous and provides a completion handler:

    - (void) userWithUsername:(NSString *)username 
                     password:(NSString *)password 
                   completion:(completion_handler_t)completion;
    

    where completion_handler_t is a type definition and may be declared in the header file like this:

    typedef void (^completion_handler_t)(User*, NSError*);
    

    Note that using a typedef is optional and may make your code more comprehensible.

    Then you can use it as follows:

    [self userWithUsername:username 
                  password:password 
                completion:^(User* user, NSError*error){
        // Check error; do something with user
        ...
    }];
    

    You can implement it as shown below:

    - (void) userWithUsername:(NSString *)username 
                     password:(NSString *)password 
                   completion:(completion_handler_t)completion
    {
        NSDictionary *params = @{@"email": username, @"password": password};
        [[DCAPIClient sharedClient] POST:@"login" parameters:params   
            success:^(NSURLSessionDataTask * __unused task, id JSONResult) {
                NSLog(@"JSON %@", JSONResult);
                BOOL errorCode = [JSONResult objectForKey:@"error"];
                if (!errorCode) {
                    self.username = [JSONResult objectForKey:@"name"];            
                    // Fill the attributes
                    // self.email = .. a
                    if (completion) {
                        completion(theUser, nil);  // completion(self, nil)?? 
                    }
                } else {
                    if (completion) {
                        completion(nil, error);
                    }
                }
    
            } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
                 if (completion) {
                     completion(nil, error);
                 }
            }];
    }