Does it necessary to go back to main thread to update UI? - ios

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.

Related

How can I make a service call in background while app is running ios

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
});

Unit test for AFNetworking Post request

I am trying to write unit test for my project which I use AFNetworking in.I use the following operation for my request:
- (void)testRegisterRequest{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
XCTAssert(Result,"Register failed!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Failure
}
As it is asynchronous it never test my XCTAssert line.
I searched a lot but I couldn't manage to find a tutorial or example for these test cases.Please help me by any tutorial link or hint.
Thanks in advance
There are a couple of things here.
You can use expectations for async tests: Asynchronous Testing With Xcode 6
You can use OHHTTPStubs to avoid unneeded network traffic and server load: Usage Examples
XCTestExpectation is a valid approach. Other testing frameworks offer different ways. For example, we can test a success response using OCHamcrest like this:
- (void)testRegisterRequest
{
__block id response = nil;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
response = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
assertWithTimeout(5, thatEventually(response), is(notNilValue()));
}
You would probably test the response for its attributes, instead of simply testing that it is not a nil value.

svprogresshud not showing on main thread

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.

Cancel Post request in Afnetworking 2.0

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] ?

Running AFNetworking 2 synchronously on a created background thread

I create a background thread to do a series of AFNetworking requests, and I want them to be in order. Some code:
dispatch_queue_t request_queue = dispatch_queue_create("someLabel", NULL);
dispatch_async(request_queue, ^{
dispatch_semaphore_wait(self.mySemaphore, DISPATCH_TIME_FOREVER);
// AFnetworking request I want to be synchronous
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"foo": #"bar"};
[manager POST:#"http://example.com/resources.json" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
...
How do I make the AFHTTPRequest synchronous?
Thanks!
In this case, you should generate the NSURLSessionDataTask yourself. The AF Operation manager will enqueue the operation immediately (that's just what it does). If you create one yourself, you can call resume when you please or add the operation to your own queue as you please.

Resources