From affc7b09c461637866705776b3b22c7680888049 Mon Sep 17 00:00:00 2001 From: Bryan Berg Date: Mon, 22 Apr 2013 16:33:43 -0700 Subject: [PATCH] Update SDK. - Returns user ID. Updated delegate methods. - Added method for sending invites. --- README.md | 8 ++--- Source/ADNLogin.h | 78 ++++++++++++++++++++++++++++++++++++++++++++--- Source/ADNLogin.m | 60 ++++++++++++++++++++++++------------ 3 files changed, 119 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0f690d9..52b9a3b 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ In your app delegate's .m file, instantiate the SDK: Ensure that the login SDK has the opportunity to handle any open URL which comes back: ```objc -- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { - return [self.adn handleOpenURL:url]; +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { + return [self.adn openURL:url sourceApplication:sourceApplication annotation:annotation]; } ``` @@ -56,11 +56,11 @@ Implement the ADNLoginDelegate protocol methods: ```objc #pragma mark - ADNLoginDelegate -- (void)adnLoginDidSucceedWithToken:(NSString *)accessToken { +- (void)adnLoginDidSucceedForUserWithID:(NSString *)userID token:(NSString *)accessToken { // ... do some stuff } -- (void)adnLoginDidFailWithMessage:(NSString *)errorMessage { +- (void)adnLoginDidFailWithError:(NSError *)error { // ... and here too } ``` diff --git a/Source/ADNLogin.h b/Source/ADNLogin.h index 57629ff..329c89a 100644 --- a/Source/ADNLogin.h +++ b/Source/ADNLogin.h @@ -25,23 +25,93 @@ @class ADNLogin; +typedef void (^ADNLoginSignupAvailableCompletionBlock)(BOOL available); + +static NSString *const kADNLoginErrorDomain = @"ADNLoginErrorDomain"; + +/** + The `ADNLoginDelegate` protocol defines the methods the ADNLogin SDK will use to communicate state with your app. Typically this will be implemented by your app delegate. + */ @protocol ADNLoginDelegate -- (void)adnLoginDidSucceedWithToken:(NSString *)accessToken; -- (void)adnLoginDidFailWithMessage:(NSString *)errorMessage; +/** + Called when fast-switching back from the App.net app with valid login credentials. + + @param userID The user ID of the logged-in user. + @param accessToken An access token authorized with the requested permissions. +*/ +- (void)adnLoginDidSucceedForUserWithID:(NSString *)userID token:(NSString *)accessToken; + +/** + Called when login has failed. + + @param error An error describing the reason for failure. +*/ +- (void)adnLoginDidFailWithError:(NSError *)error; @end -typedef void (^ADNLoginSignupAvailableCompletionBlock)(BOOL available); +/** + The primary object in the ADNLogin SDK. Generally, you will create an instance of this and store it on your app delegate. + */ @interface ADNLogin : NSObject +/** + The SDK delegate. + */ @property (weak, nonatomic) NSObject *delegate; + +/** + Whether or not ADNLogin-assisted authentication is available. This can be used to detect whether the App.net app is installed. If this is `NO`, your app should fall back to another login method, like the password flow. + */ @property (readonly, nonatomic, getter=isLoginAvailable) BOOL loginAvailable; + +/** + Request login. + + @param scopes A list of the requested authentication scopes. + + @return `YES` if the App.net was launched to request login, `NO` if it was not installed or unable to open + */ - (BOOL)loginWithScopes:(NSArray *)scopes; -- (BOOL)handleOpenURL:(NSURL *)url; + +/** + Call this method from your app delegate's `application:openURL:sourceApplication:annotation:` method. + + @param url The URL of the request + @param sourceApplication The bundle ID of the opening application + @param annotation The supplied annotation + + @return `YES` if the ADNLogin SDK handled the request, `NO` if it did not + */ +- (BOOL)openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; + +/** + Determine whether signup (without an invite) is currently available. Signup with invite link is always available in the app. + + @param completionBlock Block to be called with the result of the signup availability check. + + @discussion Makes a network call on a background thread. + */ - (void)pollForSignupAvailableWithCompletionBlock:(ADNLoginSignupAvailableCompletionBlock)completionBlock; + +/** + Request that the App.net app be installed. Right now, this is designed to open the App Store app via a URL scheme, but may be extended to use StoreKit in the future. + + @return `YES` if the App Store app was launched, `NO` if it was unable to open + */ - (BOOL)installLoginApp; +/** + Request that the App.net app send an invite to a user. The user will have the opportunity to edit the message before sending. + + @param message A message to the recipient (or nil for a default message) + @param email The email address of the user (or nil) + + @return `YES` if the App.net app was able to be launched, `NO` if it was not. + */ +- (BOOL)sendInviteWithMessage:(NSString *)message toEmail:(NSString *)email; + @end diff --git a/Source/ADNLogin.m b/Source/ADNLogin.m index e61f47a..b93cf7b 100644 --- a/Source/ADNLogin.m +++ b/Source/ADNLogin.m @@ -58,9 +58,23 @@ @implementation ADNLogin static NSString *queryStringForParameters(NSDictionary *parameters) { NSMutableArray *a = [NSMutableArray arrayWithCapacity:[parameters count]]; [parameters enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - [a addObject:[NSString stringWithFormat:@"%@=%@", - queryStringEscape(key, NSUTF8StringEncoding), - queryStringEscape(obj, NSUTF8StringEncoding)]]; + if (obj == [NSNull null]) { + return; + } + + if ([obj isKindOfClass:[NSValue class]]) { + obj = [obj stringValue]; + } + + if (![obj isKindOfClass:[NSString class]]) { + return; + } + + if ([obj length]) { + [a addObject:[NSString stringWithFormat:@"%@=%@", + queryStringEscape(key, NSUTF8StringEncoding), + queryStringEscape(obj, NSUTF8StringEncoding)]]; + } }]; return [a componentsJoinedByString:@"&"]; @@ -186,34 +200,35 @@ - (void)pollForSignupAvailableWithCompletionBlock:(ADNLoginSignupAvailableComple - (BOOL)loginWithScopes:(NSArray *)scopes { NSString *scopeString = [scopes componentsJoinedByString:@" "] ?: @""; - NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:self.clientID, @"client_id", - scopeString, @"scope", - [NSString stringWithFormat:@"%u", self.appPK], @"app_pk", - self.schemeSuffix, @"suffix", nil]; - + NSDictionary *parameters = @{@"client_id": self.clientID, @"app_pk": @(self.appPK), @"suffix": self.schemeSuffix ?: [NSNull null], @"scope": scopeString}; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://token?%@", self.loginScheme, queryStringForParameters(parameters)]]; - if ([[UIApplication sharedApplication] canOpenURL:url]) { - [[UIApplication sharedApplication] openURL:url]; - - return YES; - } - - return NO; + return [[UIApplication sharedApplication] openURL:url]; } -- (BOOL)handleOpenURL:(NSURL *)url { +- (BOOL)openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if ([url.scheme isEqualToString:self.primaryScheme]) { + if (sourceApplication != nil && !([sourceApplication isEqualToString:@"net.app.moana"] || [sourceApplication hasPrefix:@"net.app.moana."])) { + return NO; + } + NSDictionary *parameters = parametersForQueryString(url.fragment); NSString *accessToken = parameters[@"access_token"]; + NSString *userID = parameters[@"user_id"]; if (accessToken) { dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate adnLoginDidSucceedWithToken:accessToken]; + if ([self.delegate respondsToSelector:@selector(adnLoginDidSucceedForUserWithID:token:)]) { + [self.delegate adnLoginDidSucceedForUserWithID:userID token:accessToken]; + } }); } else { - NSString *error = parameters[@"error"] ?: @"Error logging in."; + NSString *errorMessage = parameters[@"error"] ?: @"Error logging in."; + NSError *error = [NSError errorWithDomain:kADNLoginErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorMessage}]; + dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate adnLoginDidFailWithMessage:error]; + if ([self.delegate respondsToSelector:@selector(adnLoginDidFailWithError:)]) { + [self.delegate adnLoginDidFailWithError:error]; + } }); } @@ -229,4 +244,11 @@ - (BOOL)installLoginApp { return [[UIApplication sharedApplication] openURL:[NSURL URLWithString:kADNLoginAppInstallURL]]; } +- (BOOL)sendInviteWithMessage:(NSString *)message toEmail:(NSString *)email { + NSDictionary *parameters = @{@"client_id": self.clientID, @"app_pk": @(self.appPK), @"suffix": self.schemeSuffix ?: [NSNull null], @"message": message ?: [NSNull null], @"email": email ?: [NSNull null]}; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://invite/send?%@", self.loginScheme, queryStringForParameters(parameters)]]; + + return [[UIApplication sharedApplication] openURL:url]; +} + @end