Can AFNetworking return data synchronously (inside a block)?

I have a function using AFJSONRequestOperation, and I want to return the result only after success. Could you point me in the right direction? I am still a little unfamiliar with blocks and AFNetworking specifically.

-(id)someFunction{ __block id data; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){ data = json; return data; // won't work } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){ }]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation: operation]; return data; // will return nil since the block doesn't "lock" the app. } 
+45
ios synchronous objective-c-blocks afnetworking
Nov 01 2018-11-11T00:
source share
6 answers

To block the execution of the main thread until the operation is completed, you can do [operation waitUntilFinished] after adding operations to the queue. In this case, you do not need return in the block; setting the __block variable will suffice.

However, I would strongly discourage forced asynchronous operations to synchronous methods. This is sometimes difficult to do around, but if you somehow structure it to be asynchronous, it will almost certainly be a way.

+56
Nov 01 2018-11-11T00:
source share

I use semaphores to solve this problem. This code is implemented in my own class inherited from AFHTTPClient .

 __block id result = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSURLRequest *req = [self requestWithMethod:@"GET" path:@"someURL" parameters:nil]; AFHTTPRequestOperation *reqOp = [self HTTPRequestOperationWithRequest:req success:^(AFHTTPRequestOperation *operation, id responseObject) { result = responseObject; dispatch_semaphore_signal(semaphore); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { dispatch_semaphore_signal(semaphore); }]; reqOp.failureCallbackQueue = queue; reqOp.successCallbackQueue = queue; [self enqueueHTTPRequestOperation:reqOp]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_release(semaphore); return result; 
+12
Oct 02 '13 at 9:13
source share

I would suggest that you do not make a synchronous method with AFNetworking (or blocks in general). A good approach is that you make another method and use the json data from the success block as an argument.

 - (void)methodUsingJsonFromSuccessBlock:(id)json { // use the json NSLog(@"json from the block : %@", json); } - (void)someFunction { AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){ // use the json not as return data, but pass it along to another method as an argument [self methodUsingJsonFromSuccessBlock:json]; } failure:nil]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation: operation]; } 
+11
Nov 01 '11 at 17:20
source share

It's worth noting that some of the AFNetworking AFClient features can still be used synchronously, which means you can still use subtleties like authorization headers and multi-page downloads.

For example:

 NSURLRequest *request = [self.client requestWithMethod: @"GET" path: @"endpoint" parameters: @{}]; NSHTTPURLResponse *response = nil; NSError *error = nil; NSData *responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error]; 

Be sure to check response.statusCode in this case, as this method does not treat HTTP failure codes as errors.

+6
Sep 20 '13 at 17:27
source share

Add this below the code you usually work with:

 [operation start]; [operation waitUntilFinished]; // do what you want // return what you want 

Example:

 + (NSString*) runGetRequest:(NSString*)frontPath andMethod:(NSString*)method andKeys:(NSArray*)keys andValues:(NSArray*)values { NSString * pathway = [frontPath stringByAppendingString:method]; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:pathway]]; NSMutableDictionary * params = [[NSMutableDictionary alloc] initWithObjects:values forKeys:keys]; NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:pathway parameters:params]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { // Success happened here so do what ever you need in a async manner } failure:^(AFHTTPRequestOperation *operation, NSError *error) { //error occurred here in a async manner }]; [operation start]; [operation waitUntilFinished]; // put synchronous code here return [operation responseString]; } 
+4
Nov 10 '13 at
source share

To expand / update @Kasik's answer. You can create a category in AFNetworking in the same way using semaphores:

 @implementation AFHTTPSessionManager (AFNetworking) - (id)sendSynchronousRequestWithBaseURLAsString:(NSString * _Nonnull)baseURL pathToData:(NSString * _Nonnull)path parameters:(NSDictionary * _Nullable)params { __block id result = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:baseURL]]; [session GET:path parameters:params progress:nil success:^(NSURLSessionDataTask *task, id responseObject) { result = responseObject; dispatch_semaphore_signal(semaphore); } failure:^(NSURLSessionDataTask *task, NSError *error) { dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return result; } @end 

If you call the synchronization block inside the completion block of another AFNetwork request, be sure to change the completionQueue property. If you do not change it, the synchronous block will call the main queue upon completion, already in the main queue, and your application will crash.

 + (void)someRequest:(void (^)(id response))completion { AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:@""] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; dispatch_queue_t queue = dispatch_queue_create("name", 0); session.completionQueue = queue; [session GET:@"path/to/resource" parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) { NSDictionary *data = [session sendSynchronousRequestWithBaseURLAsString:@"" pathToData:@"" parameters:nil ]; dispatch_async(dispatch_get_main_queue(), ^{ completion (myDict); }); } failure:^(NSURLSessionDataTask *task, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ completion (error); }); }]; 
+1
Mar 15 '16 at 20:36
source share



All Articles