On viewdidload of my tableviewcontroller i have the following
[SVProgressHUD showWithStatus:#"Loading"];
[self getData];
[SVProgressHUD dismiss];
On the getData method, i am using AFNetworking to get the data from my backend api. Since that is an asynchronous call, i would expect my SVProgressHUD to show.
-(void) getData {
AFHTTPRequestOperationManager * reqManager =AFHTTPRequestOperationManager manager];
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
//set auth token..etc
[reqManager GET:urlString parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code for success....
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//code for failure
}];
}
But it's not showing at all. So obviously i am working on my main thread. Where am i going wrong ?
The problem is not that it's not showing on the main thread, but rather that it's showing and being dismissed so quickly that you're not seeing it. This is because GET is an asynchronous method.
The solution is to adopt a completion handler pattern, namely a block of code that getData will call when the asynchronous method finishes:
- (void)getDataWithCompletionHandler:(void (^)(NSError *))completionHandler {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
//set auth token..etc
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code for success....
if (completionHandler) {
completionHandler(nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//code for failure
if (completionHandler) {
completionHandler(error);
}
}];
}
And
[SVProgressHUD showWithStatus:#"Loading"];
[self getDataWithCompletionHandler:^(NSError *error) {
[SVProgressHUD dismiss];
}];
This pattern allows you to defer the calling of dismiss until the asynchronous process is done, but it also keeps the SVProgressHUD code together in a single area (rather than scatter it about and burying UI related code inside your networking methods).
The problem is you are showing hud immediately after method call like
[self getData];
[SVProgressHUD dismiss];
This is the problem. Move dismiss code to
-(void) getData {
AFHTTPRequestOperationManager * reqManager =AFHTTPRequestOperationManager manager];
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
//set auth token..etc
[reqManager GET:urlString parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code for success....
[SVProgressHUD dismiss];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//code for failure
[SVProgressHUD dismiss];
}];
}
You can see the dismiss call in success and failure of webservice call, and call the getData method by
[SVProgressHUD showWithStatus:#"Loading"];
[self getData];
remove the [SVProgressHUD dismiss] from here.
Related
I have class that manages connections with AFNetworking.
So I want to call my function like NSDictionary *dict = [ServerManager requestWithURL:#"https://someurl.com"];
And that's the function in the other class:
- (NSDictionary *) requestWithURL:(NSString *)requestURL {
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
[manager GET:requestURL parameters:nil progress:nil
success:^(NSURLSessionDataTask *operation, id responseObject){
return responseObject;
}
failure:^(NSURLSessionDataTask *operation, NSError *error) {
}];
}
I know that is incorrect to do that. So what should I do to return responseObject back to NSDictionary *dict? I'd like to get the basic idea of asynchronous development with blocks.
Since the networking request completes long after its is launched, the only way to handle the result is with a block passed to your request method...
// when request completes, invoke the passed block with the result or an error
- (void)requestWithURL:(NSString *)requestURL completion:(void (^)(NSDictionary *, NSError *))completion {
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
[manager GET:requestURL parameters:nil progress:nil success:^(NSURLSessionDataTask *operation, id responseObject){
if (completion) completion((NSDictionary*)responseObject, nil);
}, failure:^(NSURLSessionDataTask *operation, NSError *error) {
if (completion) completion(nil, error);
}];
}
Make it public in a ServerManager.h
- (void)requestWithURL:(NSString *)requestURL completion:(void (^)(NSDictionary *, NSError *))completion;
Elsewhere, call it:
[ServerManager requestWithURL:#"http://someurl.com" completion:^(NSDictionary *dictionary, NSError *error) {
// check error and use dictionary
}];
Anyone know. how can make service call during user interacts with particular screen. I mean I need to call additional data while user interact with app. but it still look like device is hang. help me please.
For Get request use this code
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:#"http://example.com/resources.json" parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
or
NSURL *URL = [NSURL URLWithString:#"http://example.com/resources/123.json"];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:URL.absoluteString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
As you have not mentioned any code snippet, I assume that you are performing all your operations on main thread and that is the reason why your app is hanging. To be specific about main thread and background thread, we perform all the UI related tasks on main thread and background tasks on some other thread.So you can create another thread for your background task like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Perform your background task here
});
I was using AFNetworking to deal with the http request.And here is my code:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:URL_LOGIN parameters:parames success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self.tableview reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Request failed");
}];
or:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:URL_LOGIN parameters:parames success:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableview reloadData];
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Request failed");
}];
Which one is right,does it necessary to use dispathc_get_main_queue(),or the AFNetworking fix everything? Anybody knows?Thanks in advance.
On one hand, AppKit and UIKit is not thread safe, so you have to do any UI-related work on the main thread.
But as for AFNetworking, it automatically makes sure that the callbacks (success or failure) is executed on the main thread (unless you set otherwise). So normally you do not have to explicitly use dispatch_get_main_queue to dispatch your work to the main thread.
To check whether the callback is on the main queue:
[manager POST:someURL parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (operation.completionQueue == NULL) {
// is main queue
}
}...
Documentation
UI update always happens in main thread.So your tableview needs to be reloaded in main thread.
So the second one is true.
I have a POST method that looks like
+ (id) post {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"application/json"];
id response = nil;
[manager POST:#"https://mapp.com/oauth/register"
parameters:#{#"email" : #"e", #"memberExternalId" : #"m"}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
response = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// (todo) alert on failure
NSLog(#"Error: %#", error);
}];
return response;
}
I thought to abstract this method so that I could reuse it, so I created id response.
But when I assign
response = responseObject;
I get compilation error as
Variable is declared outside the block and is not assignable
Question
How can I preserve the response so that I can return it later.
You're calling an asynchronous method that uses a completion block pattern, so you should do the same. So
Change the return type to void;
Add block parameter to your method;
In the POST completion block, call your completionHandler if there was one supplied;
Thus:
+ (void) postWithCompletionHandler:(void (^)(id responseObject, NSError *error))completionHandler {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
// these lines not needed
//
// [manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
// manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"application/json"];
[manager POST:#"https://mapp.com/oauth/register"
parameters:#{#"email" : #"e", #"memberExternalId" : #"m"}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(nil, error);
}
}];
}
Thus, you'd call it like so:
[MyClass postWithCompletionHandler:^(id responseObject, NSError *error) {
if (!responseObject) {
NSLog(#"failed: %#", error);
return;
}
// use the `responseObject` here
}];
// Note, because the above is asynchronous, don't try to use `responseObject` here.
// You can only use it inside the above `completionHandler` block.
As an aside, you probably don't want to instantiate a new AFHTTPRequestOperationManager for every request. So I'd personally (a) make the AFHTTPRequestOperationManager a property of this class; (b) move the instantiation of the operation manager into some initialization method for the class; and (c) make this "perform request" method an instance method, not a class method. It's not so critical here, but as you contemplate future functionality where you're issuing many requests, it's inefficient to be instantiating new request operation managers all over the place.
Hi I am making post request using AFnetworking 2.0.
My request looks like this.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
[manager.requestSerializer setValue:#"some value" forHTTPHeaderField:#"x"];
[manager POST:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
//doing something
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// error handling.
}];
How can i cancel this request???
POST method return the AFHTTPRequestOperation operation. You can cancel it by calling cancel.
AFHTTPRequestOperation *post =[manager POST:nil parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//doing something
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// error handling.
}];
//Cancel operation
[post cancel];
Tried [manager.operationQueue cancelAllOperations] ?