From 7052f0cd7a8f900c7dce06784f506803564b0fb2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 10 Oct 2013 02:39:44 +0200 Subject: [PATCH] Require the user to call `authenticateInstallation` manually This solves multiple issues: - Possible crashes at startup because the app is already in the process of presenting another modal view. This way the developer can make sure that only one modal view is being presented - If the app is showing e.g. a login view on startup, it is now not needed to turn off automatic mode and setup the complete auth workflow manually and simply invoke `authenticateInstallation` after the login view is either fully presented (`viewDidLoad` finished) or the user did log in --- Classes/BITAuthenticator.h | 28 +++++++++++++ Classes/BITAuthenticator.m | 41 ++++++++++++++----- ...To-Authenticating-Users-on-iOS-template.md | 20 +++++++++ 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/Classes/BITAuthenticator.h b/Classes/BITAuthenticator.h index 9c677f6378..0d0f7f0d39 100644 --- a/Classes/BITAuthenticator.h +++ b/Classes/BITAuthenticator.h @@ -111,6 +111,13 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) * sure only users who are testers of your app are allowed to run it. * * This module automatically disables itself when running in an App Store build by default! + * + * @warning It is mandatory to call `authenticateInstallation` somewhen after calling + * `[[BITHockeyManager sharedHockeyManager] startManager]` or disable `automaticMode` + * and fully customize the identification and validation workflow yourself. + * If your app shows a modal view on startup, make sure to call `authenticateInstallation` + * either once your modal view is fully presented (e.g. its `viewDidLoad:` method is processed) + * or once your modal view is dismissed. */ @interface BITAuthenticator : BITHockeyBaseManager @@ -285,6 +292,27 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) /// @name Authentication ///----------------------------------------------------------------------------- +/** + * Invoked automatic identification and validation + * + * If the `BITAuthenticator` is in automatic mode this will initiate identifying + * the current user according to the type specified in `identificationType` and + * validate if the identified user is allowed to run this application. + * + * If the user is not yet identified it will present a modal view asking the user to + * provide the required information. + * + * If your app provides it's own startup modal screen, e.g. a guide or a login, then + * you might either call this method once that UI is fully presented or once + * the user e.g. did actually login already. + * + * @warning You need to call this method in your code even if automatic mode is enabled! + * + * @see identificationType + * @see automaticMode + */ +- (void) authenticateInstallation; + /** * Identifies the user according to the type specified in `identificationType`. * diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index fffebf9eda..c1ce305da5 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -50,6 +50,8 @@ static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAut id _appDidBecomeActiveObserver; id _appWillResignActiveObserver; UIViewController *_authenticationController; + + BOOL _isSetup; } - (void)dealloc { @@ -63,6 +65,7 @@ static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAut _identificationType = BITAuthenticatorIdentificationTypeAnonymous; _automaticMode = YES; + _isSetup = NO; _restrictApplicationUsage = NO; _restrictionEnforcementFrequency = BITAuthenticatorAppRestrictionEnforcementOnFirstLaunch; } @@ -74,20 +77,36 @@ static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAut //disabled in the appStore if([self isAppStoreEnvironment]) return; - switch ([[UIApplication sharedApplication] applicationState]) { - case UIApplicationStateActive: - [self authenticate]; - break; - case UIApplicationStateBackground: - case UIApplicationStateInactive: - // do nothing, wait for active state - break; - } - - [self registerObservers]; + _isSetup = YES; } #pragma mark - +- (void)authenticateInstallation { + //disabled in the appStore + if([self isAppStoreEnvironment]) return; + + // make sure this is called after startManager so all modules are fully setup + if (!_isSetup) { + [self performSelector:@selector(authenticateInstallation) withObject:nil afterDelay:0.1]; + } + + static dispatch_once_t authenticatePredicate; + dispatch_once(&authenticatePredicate, ^{ + + switch ([[UIApplication sharedApplication] applicationState]) { + case UIApplicationStateActive: + [self authenticate]; + break; + case UIApplicationStateBackground: + case UIApplicationStateInactive: + // do nothing, wait for active state + break; + } + + [self registerObservers]; + }); +} + - (void) authenticate { //when running in manual mode, we don't actually do anything ourselves if(!self.automaticMode) return; diff --git a/docs/HowTo-Authenticating-Users-on-iOS-template.md b/docs/HowTo-Authenticating-Users-on-iOS-template.md index 5dad53f08b..94d3117932 100644 --- a/docs/HowTo-Authenticating-Users-on-iOS-template.md +++ b/docs/HowTo-Authenticating-Users-on-iOS-template.md @@ -26,6 +26,22 @@ Previous versions of HockeySDK for iOS used the response of the method `UIDevice The user needs to enter the email address and password of his HockeyApp account. +The `BITAuthenticator` class doesn't do anything on its own. In addition to setting up the behavior, you also need to trigger the process yourself. + +If `automaticMode` is enabled (default), you simply need to place a call to `[[BITHockeyManager sharedHockeyManager] authenticateInstallation]` in your code. This will show a UI asking for identification details according to the chosen strategy. + +**IMPORTANT**: If your app shows a modal view on startup, make sure to call `authenticateInstallation` either once your modal view is fully presented (e.g. its `viewDidLoad:` method is processed) or once your modal view is dismissed. + +If `automaticMode` is disabled, you need to implement your own workflow by using + + - (void) identifyWithCompletion:(void(^)(BOOL identified, NSError *error)) completion; + +to identify the current user depending on your strategy and + + - (void) validateWithCompletion:(void(^)(BOOL validated, NSError *error)) completion; + +to validate the user may still use the app if required. + The following sections explain the different strategies and their advantages / disadvantages. @@ -35,6 +51,7 @@ Initialize HockeySDK with the following code: [[BITHockeyManager sharedHockeyManager] configureWithIdentifier:@"<#APP_ID#>" delegate:self]; [[BITHockeyManager sharedHockeyManager] startManager]; + [[BITHockeyManager sharedHockeyManager] authenticateInstallation]; Replace APP_ID with the your App ID (can be found on the app page). @@ -57,6 +74,7 @@ Initialize HockeySDK with the following code: [[BITHockeyManager sharedHockeyManager] configureWithIdentifier:@"<#APP_ID#>" delegate:self]; [[BITHockeyManager sharedHockeyManager].authenticator setIdentificationType:BITAuthenticatorIdentificationTypeDevice]; [[BITHockeyManager sharedHockeyManager] startManager]; + [[BITHockeyManager sharedHockeyManager] authenticateInstallation]; Replace APP_ID with the your App ID (can be found on the app page). @@ -104,6 +122,7 @@ Initialize HockeySDK with the following code: [[BITHockeyManager sharedHockeyManager].authenticator setAuthenticationSecret:@"<#SECRET#>"]; [[BITHockeyManager sharedHockeyManager].authenticator setIdentificationType:BITAuthenticatorIdentificationTypeHockeyAppEmail]; [[BITHockeyManager sharedHockeyManager] startManager]; + [[BITHockeyManager sharedHockeyManager] authenticateInstallation]; Replace APP_ID with the your App ID and SECRET with the Secret (both values can be found on the app page). @@ -127,6 +146,7 @@ Initialize HockeySDK with the following code: [[BITHockeyManager sharedHockeyManager] configureWithIdentifier:@"APP_ID" delegate:self]; [[BITHockeyManager sharedHockeyManager].authenticator setIdentificationType:BITAuthenticatorIdentificationTypeHockeyAppUser]; [[BITHockeyManager sharedHockeyManager] startManager]; + [[BITHockeyManager sharedHockeyManager] authenticateInstallation]; Replace APP_ID with the your App ID (can be found on the app page).