iphone - solucion - no se puede activar la biblioteca musical de icloud
AFNetworking: ¿Cómo se pueden volver a intentar las solicitudes de configuración en el caso de un tiempo de espera? (4)
Recientemente he migrado de ASIHTTPRequest a AFNetworking, lo que ha sido excelente. Sin embargo, el servidor con el que me estoy conectando tiene algunos problemas y, a veces, hace que se agoten mis solicitudes. Al usar ASIHTTPRequest fue posible configurar un conteo de reintentos en una solicitud en el caso de un tiempo de espera con el siguiente selector
-setNumberOfTimesToRetryOnTimeout:
A esto se puede hacer referencia más adelante en esta publicación, ¿Se puede volver a intentar una solicitud ASIHTTP?
Esto es AFNetworking si no está familiarizado con https://github.com/AFNetworking/AFNetworking#readme
No he podido encontrar una api equivalente en AFNetworking. ¿Alguien ha encontrado una solución para volver a intentar las solicitudes de red en caso de que se agote el tiempo de espera usando AFNetworking?
El desarrollador de Matt Thompson de AFNetworking tuvo la amabilidad de responder esto por mí. A continuación se muestra el enlace github explicando la solución.
https://github.com/AFNetworking/AFNetworking/issues/393
Básicamente, AFNetworking no admite esta funcionalidad. Queda a cargo del desarrollador implementar caso por caso, como se muestra a continuación (tomado de la respuesta de Matt Thompson en 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];
}];
}
}
En mi caso, con frecuencia requería la funcionalidad de reintento, por lo que se me ocurrió esta categoría de política de reintentos que lo ayudará con AFNetworking+RetryPolicy
Con respecto a AFNetworking 3.0 podría servir bien.
Implementé el método privado en mi clase de 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);
}];
}
Según sus respuestas, podría hacer algo aún más genérico (y complicado) utilizando 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 rodee sus solicitudes 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 manera, los reintentos esperan a que finalice la solicitud HTTP y no tiene que implementar el ciclo de reintentos en cada método de solicitud.