Tiago Almeida
Tiago Almeida

Reputation: 263

MBProgressHUD And NSURLConnection

I have the following question.. I made an API class with all my functions and they return the result when the url request is finished.

I want to make an loader before the request is made and remove it after it's done.

My request code is this:

-(NSDictionary *)makeApiCall:(NSString *)function params:(NSDictionary *)params view:(UIView *)displayView{

__block NSDictionary *json = nil;

[self makeRequestTo:function parameters:params successCallback:^(id jsonResponse) {
    json = jsonResponse;
} errorCallback:^(NSError *error, NSString *errorMsg) {
    NSLog(@"Error: %@", errorMsg);
}];


return json;

}


- (void) makeRequestTo:(NSString *) function
        parameters:(NSDictionary *) params
   successCallback:(void (^)(id jsonResponse)) successCallback
     errorCallback:(void (^)(NSError * error, NSString *errorMsg)) errorCallback {

NSURLResponse *response = nil;
NSError *error = nil;

AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate setLoadingEnabled:true]
NSURL *url = [NSURL URLWithString:kBaseUrl];

AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];

httpClient.parameterEncoding = AFFormURLParameterEncoding;

NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:[NSString stringWithFormat:@"%@/%@",kApiBaseUrl,function] parameters:params];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if(error) {
        errorCallback(error, nil);
    } else {
        id JSON = AFJSONDecode(data, &error);
        successCallback(JSON);
    }
}];

}

But.. this won't work. The loader only appear after the request is complete and it is removed within a second.

Funny thing, if i call it out from this Api class, it works.. Can anyone help me out?

Thanks

Upvotes: 1

Views: 336

Answers (1)

Martin R
Martin R

Reputation: 539725

Updates to the user interface are only done when the program control returns to the main runloop. But the synchronous call

NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

blocks the main thread and does not return before the request has been completed. Therefore no UI updates are done, in particular your loader is not displayed.

You could use sendAsynchronousRequest instead and handle the response (and hide the loader) in the completion block, so the code would roughly look like this:

- (void) makeRequestTo:(NSString *) function
            parameters:(NSDictionary *) params
       successCallback:(void (^)(id jsonResponse)) successCallback
         errorCallback:(void (^)(NSError * error, NSString *errorMsg)) errorCallback {

    AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate showLoader];

    // ... create request ...

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        [appDelegate hideLoader];

        // ... handle response ...

    }];
}

But there is another problem with your makeApiCall: method: Since makeRequestTo: works asynchronously, it returns almost immediately . The completion block with json = jsonResponse; is called later, when the request has been completed. Therefore the json variable will always been nil.

And there is no way around that. You cannot wrap an asynchronous call into a synchronous method without blocking the thread. In particular, makeApiCall: cannot return the response dictionary. You will have to change your program logic to work asynchronously.

Upvotes: 2

Related Questions