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
How can I signal my semaphore in the latter case? If I add an observer to the authentication.assessToken will it be fired?
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.