Reputation: 263
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
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