Skip to content

Instantly share code, notes, and snippets.

@bobspryn
Created March 19, 2014 04:20
Show Gist options
  • Select an option

  • Save bobspryn/9635370 to your computer and use it in GitHub Desktop.

Select an option

Save bobspryn/9635370 to your computer and use it in GitHub Desktop.
TwitterAuth.m
self.accountStore = [[ACAccountStore alloc] init];
self.pickTwitterAccountCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(ACAccount *account) {
return [RACSignal return:account];
}];
// Returns an error we handle when they cancel picking from multiple twitter accounts
RACSignal *cancelledPickingTwitterAccount = [[[self.pickTwitterAccountCommand.executionSignals switchToLatest]
filter:^BOOL(ACAccount *account) {
return account == nil;
}]
flattenMap:^RACStream *(id value) {
return [RACSignal error:[NSError errorWithDomain:ThreeCentsErrorDomain code:ThreeCentsErrorCancelledTwitterLogin userInfo:nil]];
}];
// WHen they select a twitter account from the multiple accounts
RACSignal *selectedTwitterAccount = [[self.pickTwitterAccountCommand.executionSignals switchToLatest]
filter:^BOOL(ACAccount *account) {
return account != nil;
}];
// Returns all twitter accounts for this iPhone
RACSignal *allTwitterAccounts = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
ACAccountType *twitterType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[_accountStore requestAccessToAccountsWithType:twitterType options:NULL completion:^(BOOL granted, NSError *error) {
@strongify(self);
if (granted) {
NSArray *accounts = [self.accountStore accountsWithAccountType:twitterType];
if (accounts.count > 0) {
[subscriber sendNext:accounts];
[subscriber sendCompleted];
} else {
[subscriber sendError:[NSError errorWithDomain:ThreeCentsErrorDomain code:ThreeCentsErrorNoTwitterAccounts userInfo:nil]];
}
} else {
[subscriber sendError:[NSError errorWithDomain:ThreeCentsErrorDomain code:ThreeCentsErrorTwitterAccessDenied userInfo:nil]];
}
}];
return nil;
}];
// Just pairs the current twitter ID with the twitter accounts
RACSignal *allAccountsWithTwitterID = [allTwitterAccounts zipWith:[RACSignal return:me.twitterUserID]];
// Found an account matching the existing twitter ID
RACSignal *matchingTwitterAccountSignal = [[[allAccountsWithTwitterID
filter:^BOOL(RACTuple *values) {
NSNumber *twitterID = values.second;
return twitterID != nil;
}]
flattenMap:^RACStream *(RACTuple *values) {
RACTupleUnpack(NSArray *accounts, NSNumber *twitterID) = values;
for (ACAccount *account in accounts) {
NSString *userID = [account valueForKeyPath:@"properties.user_id"];
if ([userID isEqualToString:twitterID.stringValue]) {
return [RACSignal return:account];
}
}
return [RACSignal error:[NSError errorWithDomain:ThreeCentsErrorDomain code:ThreeCentsErrorTwitterAccountMissing userInfo:nil]];
}]
ignore:nil];
// No account previously selected
RACSignal *noCurrentTwitterAcccount = [[allAccountsWithTwitterID
filter:^BOOL(RACTuple *values) {
NSNumber *twitterID = values.second;
return twitterID == nil;
}]
reduceEach:^id (NSArray *accounts, NSNumber *twitterID){
return accounts;
}];
// Authorizing for the first time and only found one account
RACSignal *twitterAccountsReturnedOnlyOneSignal = [[noCurrentTwitterAcccount
filter:^BOOL(NSArray *array) {
return array.count == 1;
}]
map:^id(NSArray *accounts) {
return accounts.firstObject;
}];
// we arrive at an account one way or another
RACSignal *accountSignal = [RACSignal merge:@[twitterAccountsReturnedOnlyOneSignal,
selectedTwitterAccount,
cancelledPickingTwitterAccount,
matchingTwitterAccountSignal]];
self.authorizeTwitterCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
// Assign multiple twitter accounts to this array which the VC will observe
RAC(self, twitterAccountsToSelectFrom) = [[noCurrentTwitterAcccount
filter:^BOOL(NSArray *twitterAccounts) {
return twitterAccounts.count > 1;
}]
catchTo:[RACSignal return:nil]];
return [[[[[accountSignal
take:1]
flattenMap:^RACStream *(ACAccount *account) {
return [[TCAPI fetchRequestToken] zipWith:[RACSignal return:account]];
}]
flattenMap:^RACStream *(RACTuple *values) {
RACTupleUnpack(NSString *reverseAuthSignature, ACAccount *account) = values;
return [[TCAPI fetchTwitterOauthTokenWithAccount:account reverseAuthSignature:reverseAuthSignature] zipWith:[RACSignal return:account]];
}]
// now we have the token, store with our server
flattenMap:^RACStream *(RACTuple *values) {
RACTupleUnpack(NSDictionary *twitterData, ACAccount *account) = values;
return [[TCAPI addUpdateTwitterAccountToken:twitterData[kTCTwitterOAuthTokenKey] secret:twitterData[kTCTwitterOAuthTokenSecretKey]]
mapReplace:account];
}]
catch:^RACSignal *(NSError *error) {
if ([error.domain isEqualToString:ThreeCentsErrorDomain]) {
return [RACSignal error:error];
}
return [RACSignal error:[NSError errorWithDomain:ThreeCentsErrorDomain code:ThreeCentsErrorTwitterLoginFailed userInfo:nil]];
}];
}];
@bobspryn
Copy link
Copy Markdown
Author

Basically this identifies either a twitter account matching an existing user ID, finds a singular twitter account, or sets the twitterAccountsToSelectFrom property so the user can choose from multiple. The command subscribes to the result of any of those (or an error) and does some API work retrieving and updating a token before returning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment