AFNetworking: ¿Cómo configurar las requestes que se deben volver a intentar en caso de que se agote el time de espera?

Recientemente he migrado desde ASIHTTPRequest a AFNetworking, lo cual ha sido excelente. Sin embargo, el server con el que me estoy conectando tiene algunos problemas y, a veces, ocasiona que mis requestes expiren. Cuando se usa ASIHTTPRequest, fue posible configurar un recuento de rebashs en una request en el caso de un time de espera con el siguiente selector

-setNumberOfTimesToRetryOnTimeout: 

Esto se puede hacer reference adicional en esta publicación, ¿Se puede volver a intentar un ASIHTTPRequest?

Esto es AFNetworking si no está familiarizado https://github.com/AFNetworking/AFNetworking#readme

No pude encontrar una API api equivalente en AFNetworking, ¿alguien ha encontrado una solución para reintentar requestes de networking en caso de time de espera utilizando AFNetworking?

El desarrollador de AFNetworking de Matt Thompson tuvo la amabilidad de responder esto por mí. A continuación se muestra el enlace de github que explica la solución.

https://github.com/AFNetworking/AFNetworking/issues/393

Básicamente, AFNetworking no admite esta funcionalidad. El desarrollador debe implementar caso por caso, como se muestra a continuación (tomado de la respuesta de Matt Thompson sobre github)

 - (void)downloadFileRetryingNumberOfTimes:(NSUInteger)ntimes success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure { if (ntimes <= 0) { if (failure) { NSError *error = ...; failure(error); } } else { [self getPath:@"/path/to/file" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { if (success) { success(...); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { [self downloadFileRetryingNumberOfTimes:ntimes - 1 success:success failure:failure]; }]; } } 

Implementé un método privado en mi class ApiClient:

 - (void)sendRequest:(NSURLRequest *)request successBlock:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successBlock failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failureBlock { __block NSUInteger numberOfRetries = 3; __block __weak void (^weakSendRequestBlock)(void); void (^sendRequestBlock)(void); weakSendRequestBlock = sendRequestBlock = ^{ __strong typeof (weakSendRequestBlock)strongSendRequestBlock = weakSendRequestBlock; numberOfRetries--; AFHTTPRequestOperation *operation = [self.httpManager HTTPRequestOperationWithRequest:request success:successBlock failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSInteger statusCode = [[[error userInfo] objectForKey:AFNetworkingOperationFailingURLResponseErrorKey] statusCode]; if (numberOfRetries > 0 && (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 0)) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ strongSendRequestBlock(); }); } else { if (failureBlock) { failureBlock(operation, error); } } }]; [self.httpManager.operationQueue addOperation:operation]; }; sendRequestBlock(); } 

Ejemplo de uso:

 - (void)getSomeDetails:(DictionaryResultBlock)block { if (!block) { return; } NSString *urlString = @"your url string"; NSMutableURLRequest *request = [self.httpManager.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:urlString relativeToURL:self.defaultUrl] absoluteString] parameters:nil error:nil]; // Configure you request here [request setValue:version forHTTPHeaderField:@"client-version"]; NSMutableDictionary *bodyParams = @{}; [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:bodyParams options:0 error:nil]]; [self sendRequest:request successBlock:^(AFHTTPRequestOperation *operation, id responseObject) { id response = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil]; block(response, nil); } failureBlock:^(AFHTTPRequestOperation *operation, NSError *error) { block(nil, error); }]; } 

En mi caso, con frecuencia requería volver a intentar la funcionalidad, así que llegué a esta categoría de política de rebashs que lo ayudaría con esa AFNetworking + RetryPolicy

Con respecto a AFNetworking 3.0 , podría funcionar bien.

Según tus respuestas, podrías hacer algo aún más genérico (y complicado) al usar un bloque tomando como parámetro un bloque:

 typedef void (^CallbackBlock)(NSError* error, NSObject* response); - (void) performBlock:(void (^)(CallbackBlock callback)) blockToExecute retryingNumberOfTimes:(NSUInteger)ntimes onCompletion:(void (^)(NSError* error, NSObject* response)) onCompletion { blockToExecute(^(NSError* error, NSObject* response){ if (error == nil) { onCompletion(nil, response); } else { if (ntimes <= 0) { if (onCompletion) { onCompletion(error, nil); } } else { [self performBlock:blockToExecute retryingNumberOfTimes:(ntimes - 1) onCompletion:onCompletion]; } }; }); } 

Luego rodea tus requestes HTTP asíncronas como las siguientes:

 [self performBlock:^(CallbackBlock callback) { [...] AFHTTPRequestOperationManager *manager = [WSManager getHTTPRequestOperationManager]; [manager POST:base parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { dispatch_async(dispatch_get_main_queue(), ^(void){ if (callback) { callback(nil, responseObject); } }); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (callback) { NSError* errorCode = [[NSError alloc] initWithDomain:AppErrorDomain code:[operation.response statusCode] userInfo:@{ NSLocalizedDescriptionKey :error.localizedDescription}]; callback(errorCode, nil); } }]; } retryingNumberOfTimes:5 onCompletion:^(NSError *error, NSObject* response) { //everything done }]; 

De esta forma, los rebashs esperan que finalice la request HTTP y no tiene que implementar el ciclo de rebashs en cada método de request.