等待异步调用在完成块内完成

时间:2017-11-22 12:11:34

标签: ios objective-c authentication

我目前正在开发一款POC应用I have previously posted about it here。我试图处理身份验证令牌的自动刷新,如果我的服务器给我401错误(未经授权)。

这是我的演示函数,它从服务器请求一些信息(我可以故意发送它有效/无效的auth令牌)

NSInteger retryAttempts = 0;
NSInteger retryMax = 1;

- (void) requestDataForUser {
    NSLog(@"requestDataForUser - Called");

    //Indicate Network Activity
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
    });

    //Build request URL String
    NSString *requestString = [NSString stringWithFormat:@"%@%@%@",baseURL,requestURL,@"3"];//Change to allow change in username here.

    //Get auth token
    NSString *accessToken = [SAMKeychain passwordForService:kServer account:kKeyAccessToken];
    NSString *requestAuthorization = [NSString stringWithFormat:@"%@ %@", @"Bearer", accessToken];

    //Initialize url request
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

    //Set the url for the request
    [request setURL:[NSURL URLWithString:requestString]];

    //Set HTTP method for request
    [request setHTTPMethod:@"GET"];

    //Set HTTP header field with the authorization token
    [request setValue:requestAuthorization forHTTPHeaderField:@"Authorization"];

    //Create full request
    NSURLSession *session = [NSURLSession sharedSession];

    __weak typeof (self) weakSelf = self;
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"Status Code: %ld\n",(long)httpResponse.statusCode);

                                                    NSString *message = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode];
                                                    NSLog(@"Message: %@", message);

                                                    NSLog(@"requestDataForUser - Responce from server");

                                                    //Check for an error, if there is no error we proceed.
                                                    if (!error) {

                                                        if (retryAttempts <= retryMax) {
                                                        switch (httpResponse.statusCode) {
                                                            case 200 ... 299:
                                                                NSLog(@"SUCCESS");
                                                                NSLog(@"Performing any completion related functions!");
                                                                break;
                                                            case 401:
                                                                NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);

                                                                [weakSelf refreshAuth];
                                                                [weakSelf requestDataForUser];//retries this function

                                                                retryAttempts += 1;
                                                                break;

                                                        }}
                                                        else {
                                                            NSLog(@"401 Error Recieved - Retried credentials %ld time(s), please check your details are correct", (long)retryMax);
                                                            retryAttempts = 0; //Reset retry counter
                                                            //Alert controller?
                                                        }
                                                        //Get que and perform any UI changes
                                                        dispatch_async(dispatch_get_main_queue(), ^{

                                                            [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE;
                                                        });
                                                    }
                                                    else {
                                                        //Failed request
                                                        NSLog(@"requestDataForUser - error : %@", error.description);
                                                        dispatch_async(dispatch_get_main_queue(), ^{
                                                            [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE;
                                                        });
                                                    }
                                                }];
    [dataTask resume];

}

我遇到的问题出现在请求的401挑战部分。我想要做的是请求/刷新一个新令牌(在最后一次迭代中刷新但当前我的服务器在令牌刷新时有点击中/未命中,因此我在此示例中请求新令牌)。那么让我们来看看我的服务器挑战部分:

case 401:
         NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);
         [weakSelf refreshAuth];
         [weakSelf requestDataForUser];//retries this function
         retryAttempts += 1;
         break;

所以我在这里打印出尝试号码,我可以手动设置这个&#39;阻止&#39;重试,直到它放弃并向用户抛出错误。接下来,它将调用auth令牌,重试请求并将retryAttempts增加1。

我的问题是,当我请求一个新令牌时,我会异步执行此请求,然后发送请求然后我的函数重试(显然没有新令牌),然后它会抛出错误。然后我的令牌返回并打印到控制台,新令牌成功返回。

我看过信号量,但我似乎无法让它们工作(因为我的requestAuthToken方法没有完成块)。无论如何我可以强制auth请求同步吗?

我还试图让我的requestAuth方法返回一个BOOL并在401块中循环bool直到它变为true,但是它永远不会被设置为true而while循环会一直持续。

感谢任何和所有帮助!

1 个答案:

答案 0 :(得分:0)

假设您可以更改requestAuth的实现,请向requestAuth函数添加完成处理程序参数。
在requestAuth实现中,在收到令牌后调用该处理程序。然后在requestDataForUser中:

cs

否则,使用NSOperationQueue并将最大并发操作设置为1:

case 401:
     NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);
     [weakSelf refreshAuth withCompletionHandler:weakself.requestDataForUser];
     retryAttempts += 1;
     break;