AFNetworking - How to setup requests to be retried in the event of a timeout?

前端 未结 4 756
攒了一身酷
攒了一身酷 2020-12-12 16:01

I have recently migrated from ASIHTTPRequest to AFNetworking, which has been great. However, the server that I am connecting with has some issues and sometimes causes my req

相关标签:
4条回答
  • 2020-12-12 16:10

    Based on your answers, you could do something even more generic (and tricky) by using a block taking as parameter a block :

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

    Then surround your asynchronous HTTP requests like the following :

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

    This way the retries wait for the HTTP request to finish and you don't have to implement the retry loop in each request methods.

    0 讨论(0)
  • 2020-12-12 16:12

    In my case, I frequently required retry functionality so I came up wit this retry policy category that will help you with that AFNetworking+RetryPolicy

    With respect to AFNetworking 3.0 it could serve well.

    0 讨论(0)
  • 2020-12-12 16:20

    Matt Thompson developer of AFNetworking was kind enough to answer this for me. Below is the github link explaining the solution.

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

    Basically, AFNetworking doesn't support this functionality. It is left to the developer to implement on a case by case basis as shown below (taken from Matt Thompson's answer on 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];
            }];
        }
    }
    
    0 讨论(0)
  • 2020-12-12 16:29

    I implemented private method in my ApiClient class:

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

    Example of usage:

    - (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);
            }];
        }
    
    0 讨论(0)
提交回复
热议问题