How can I make a synchronous request using the AFNetworking library. I've tried this link
https://github.com/paulmelnikow/AFNetworking-Synchronous/blob/master/AFHTTPClient%2BSynchronous.m
But it seems it isn't reliable. Sometime the request runs async using this approach.
I've a bunch of operations that fetch data from the server, each operation fetches the data synchronously since the operation itself is already in background. How can I do this using AFNetworking?
Thanks in advance for your help.
H.
The short answer is that you shouldn't do networking synchronously.
This falls under the category of "general usage question" as outlined in CONTRIBUTING:
GitHub Issues is for reporting bugs in AFNetworking and discussing features. Please check our Frequently Asked Questions page and past issues before reporting any bugs.
Please do not post any general usage questions to GitHub Issues, but instead take them to an appropriate forum such as Stack Overflow.
If I'm working on the main thread I know I should not do networking synchronously. In my case, I'm working on background threads and each thread has to fetch some data from the server. That's why I need to do it synchronously(I'm not working on the main thread so no need to do the job asynchronously). As I understood, there is no nothing in AFNetworking can do my job right?
@maystroh It wouldn't make sense to use AFNetworking if you want synchronous requests. Instead see -[NSURLConnection sendSynchronousRequest:returningResponse:error].
Anyway, as Mattt said. This issue tracker is not a forum for questions, you should try Stack Overflow.
While it makes sense to encourage users to use the async methods, why prevent experienced users from using AFNetworking in background threads synchronously? It feels lame having to rely on NSURLConnection for sync requests while AFNetworking is such a great framework.
@eneko Synchronous behavior can always be accomplished from asynchronous execution using GCD or other thread scheduling maneuvers. Nothing is preventing anyone truly determined to do networking synchronously from doing so.
There are some API infrastructure conditions to be checked in a "synchronous" way, let's say your API infrastructure status. So how to handle this?
I agree with @mattt you have to make networking asynchronous or you should try to follow this pattern. So how to handle this condition?
It's very simple: just track the status of you API in your API helper singleton instance like:
// this normally would be a NSInteger enum.
NSInteger lastAPIStatus = [MyAPIHelper sharedInstance].apiStatus;
and update it asynchronously:
MXMAPI *weakSelf = self;
[self.requestManager GET:[urlObj absoluteString] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (responseObject == nil) {
weakSelf.apiStatus = kMXMAPIStatusUnknown;
}
else {
if([responseObject valueForKey:@"status"]) {
weakSelf.apiStatus = [[responseObject valueForKey:@"status"] integerValue];
}
else {
weakSelf.apiStatus = kMXMAPIStatusUnknown;
}
}
[[MXMLogger sharedLogger] info:@"API Status %d", weakSelf.apiStatus ];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
/// handle error
}];
I know the networking asynchronous which is good. However, sometime we need to do something synchronously. There is a case that I am facing which I need to make a ios app for browsing latest information. It means that I need to sure every latest data from server should stored into sqlite when they open the apps.
@jackyshek You should update those information in a background task i.e
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
If you use a good sqlite database wrapper like https://github.com/ccgus/fmdb
you can take advantage of
So as soon as you get data back from AFNetworking:
[self.requestManager GET:self.requestUrl parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSInteger statusCode = operation.response.statusCode;
[self requestFinished:responseObject andStatusCode:statusCode];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self requestFailed:error];
}];
you should be able to do something like
-(void) requestFinished:(NSDictionary*)dict andStatusCode:(NSInteger)statusCode {
//// parse the response JSON dict
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [dict objectForKey@"key1"]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [dict objectForKey@"key2"]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [dict objectForKey@"key3"]];
if (whoopsSomethingWrongHappened) {
*rollback = YES;
return;
}
// etc…
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [dict objectForKey@"status"]];
}];
actually, i know how to do it. But asynchronous pattern is useless for me in my current project.
You can use a semaphore, like this:
NSParameterAssert(_requestManager); // prevent infinite loop
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[_requestManager GET:@"resource" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// other stuff
dispatch_semaphore_signal(semaphore);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// other stuff
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
This'll only work if you're on a secondary thread, of course, which is the only time you should be using synchronous networking.
If you're writing an iOS app and doing this from the main thread, it's critical you work asynchronously. You must never block the main thread on networking; it's all but guaranteed to crash when run without a debugger. Instead, use something like MBProgressHUD:
I had forgotten about this thread, but since it seems like this is still an issue for some users, I'll post how I managed to do this.
The solution is actually very simple. Since AFNetworking uses NSOperations, you can just wait for the operation to complete, like this:
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:nil failure:nill];
[operation start];
[operation waitUntilFinished];
// You can now access operation.responseJSON and others
Using this code on the main thread will block the user interface, since the waitUntilFinished call blocks the current thread. This code is intended to be used in asynchronous blocks, operations or background threads.
tewha's advice is work for me Thank you very much. I have try to use operation.waitUntilFinished, but which is not work for me. Maybe my code has something. I will try eneko's advice. Thank you very much for both of you.
[operation waitUntilFinished] will wait until the operation is finished, but not until the completion block has run. The sequence works like this:
When that happens, these things all happen in an undefined order:
waitUntilFinished returns.You can not rely on the completion block being finished after waitUntilFinished unless you make other arrangements, like the semaphore approach.
A few years ago I wrote a library to solve this problem, and have just updated it for AFN 3.x, adapting @tewha's suggestion. Feel free to check it out, and let me know if you have any issues:
https://github.com/paulmelnikow/AFNetworking-Synchronous
My original motivation was working with NSDocument on OS X. NSDocument supports loading documents in the background, but when you use this, it handles threading for you and provides a synchronous method to override with your loading logic. Most file IO is synchronous, so this makes sense. However I needed to load data from a rest API instead. This category was the clearest way to do that.
It may be useful in similar situations where threading is provided by another framework. It's certainly not my preferred way to do networking.
Most helpful comment
A few years ago I wrote a library to solve this problem, and have just updated it for AFN 3.x, adapting @tewha's suggestion. Feel free to check it out, and let me know if you have any issues:
https://github.com/paulmelnikow/AFNetworking-Synchronous
My original motivation was working with NSDocument on OS X. NSDocument supports loading documents in the background, but when you use this, it handles threading for you and provides a synchronous method to override with your loading logic. Most file IO is synchronous, so this makes sense. However I needed to load data from a rest API instead. This category was the clearest way to do that.
It may be useful in similar situations where threading is provided by another framework. It's certainly not my preferred way to do networking.