Pergunta

I use the following method to attempt to synchronously obtain an OAuth access token within 10 seconds, otherwise return nil. It works fine, however as an exercise I would like to convert my code to use a semaphore.

The Runloop version

- (NSString*)oAuthAccessToken
{
    @synchronized (self)
    {
        NSString* token = nil;
        _authenticationError = nil;
        if (_authentication.accessToken)
        {
            token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
        }
        else
        {
            [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:_keychainName authentication:_authentication];
            [_authentication authorizeRequest:nil delegate:self didFinishSelector:@selector(authentication:request:finishedWithError:)];
            for (int i = 0; i < 5; i++)
            {
                [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
                if (_authentication.accessToken)
                {
                    token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
                    break;
                }
                else if (_authenticationError)
                {
                    break;
                }
            }
        }
//        LogDebug(@"Returning token: %@", token);
        return token;
    }
}

Semaphore Version

The semaphore version of the code goes a little something like this:

- (NSString*)oAuthAccessToken
{
    @synchronized (self)
    {
        NSString* token = nil;
        _authenticationError = nil;
        if (_authentication.accessToken)
        {
            token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
        }
        else
        {
            _authorizationSemaphore = dispatch_semaphore_create(0);
            dispatch_async(_authorizationRequestQueue, ^(void)
            {
                [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:_keychainName authentication:_authentication];
                [_authentication authorizeRequest:nil delegate:self didFinishSelector:@selector(authentication:request:finishedWithError:)];
            });
            dispatch_semaphore_wait(_authorizationSemaphore, DISPATCH_TIME_FOREVER);
            if (_authentication.accessToken)
            {
                token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
            }
        }
        return token;
    }
}

Gotcha!!! GTMOAuth2 sometimes returns immediately

  • When GTMOAuth2 needs to hit the network it calls back via a delegate method. In this method I signal my semaphore.
  • Sometimes GTMOAuth2 is able to return immediately. The problem is the method returns void.

How can I signal my semaphore in the latter case? If I add an observer to the authentication.assessToken will it be fired?

Foi útil?

Solução

I'm not familiar with the GTMOAuth2 library but authentication.accessToken is a property, so and there doesn't seem to be anything that prevents it from being KVO compliant. Adding an observer should work for you in all cases, both for the async and the sync. Therefore, I'd consider only the async case.

If you want to make your solution even cleaner, then you should definitely try Reactive Cocoa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top