¿Puede AFNetworking devolver datos de forma síncrona (dentro de un bloque)?

Tengo una function con AFJSONRequestOperation, y deseo devolver el resultado solo después del éxito. ¿Podrías apuntarme en la dirección correcta? Todavía estoy un poco despistado con los bloques y AFNetworking específicamente.

-(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. } 

Para bloquear la ejecución del hilo principal hasta que se complete la operación, puede hacer [operation waitUntilFinished] después de que se agregue a la queue de operaciones. En este caso, no necesitaría la return en el bloque; establecer la variable __block sería suficiente.

Dicho esto, desaconsejaré enérgicamente forzar las operaciones asíncronas a methods síncronos. Es difícil captar la cabeza a veces, pero si hay alguna forma de estructurar esto para que sea asíncrono, casi seguro que sería el path a seguir.

Estoy usando los semáforos para resolver este problema. Este código se implementa en mi propia class henetworkingada de 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; 

Sugiero que no haga un método síncrono con AFNetworking (o bloques en general). Un buen enfoque es hacer otro método y usar los datos json del bloque de éxito como argumento.

 - (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]; } 

Vale la pena señalar que algunas funciones del AFClient de AFNetworking se pueden usar de manera sincrónica, lo que significa que aún puede utilizar detalles como los encabezados de autorización y las cargas multiparte.

Por ejemplo:

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

Recuerde verificar response.statusCode en este caso, ya que este método no considera los códigos de error HTTP como errores.

Agregue a continuación el código con el que normalmente trabaja:

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

Ejemplo:

 + (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 occurnetworking here in a async manner }]; [operation start]; [operation waitUntilFinished]; // put synchronous code here return [operation responseString]; } 

Para ampliar / actualizar la respuesta de @ Kasik. Puede crear una categoría en AFNetworking así usando semáforos:

 @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 

Si está llamando al bloque de synchronization dentro de un bloque de finalización de otra request AFNetwork, asegúrese de cambiar la propiedad completionQueue . Si no lo cambia, el bloque síncrono llamará a la queue principal cuando finalice la queue principal y bloqueará su aplicación.

 + (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); }); }];