From 7052f0cd7a8f900c7dce06784f506803564b0fb2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 10 Oct 2013 02:39:44 +0200 Subject: [PATCH 1/3] 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). From 498bd62cbb98acfd9bb7808b98d31c5661cca132 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 10 Oct 2013 12:32:29 +0200 Subject: [PATCH 2/3] Remove `automaticMode` property since it is not needed any more The developer needs to call at least `authenticateInstallation` instead. --- Classes/BITAuthenticator.h | 28 ++++--------------- Classes/BITAuthenticator.m | 4 --- ...To-Authenticating-Users-on-iOS-template.md | 4 +-- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/Classes/BITAuthenticator.h b/Classes/BITAuthenticator.h index 0d0f7f0d39..721e1c5560 100644 --- a/Classes/BITAuthenticator.h +++ b/Classes/BITAuthenticator.h @@ -113,8 +113,8 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) * 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. + * `[[BITHockeyManager sharedHockeyManager] startManager]` or 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. @@ -138,23 +138,6 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) */ @property (nonatomic, assign) BITAuthenticatorIdentificationType identificationType; -/** - Defines if the BITAuthenticator automatically identifies the user and also - checks if he's still allowed to use the app (depending on `restrictApplicationUsage`) - - If this is set to NO, it's your responsiblity to call - - - (void) identifyWithCompletion:(void(^)(BOOL identified, NSError *error)) completion; - - and - - - (void) validateWithCompletion:(void(^)(BOOL identified, NSError *error)) completion; - - at approciate times and also block the application or re-identify the user if validation failed. - - _Default_: `YES` - */ -@property (nonatomic, assign) BOOL automaticMode; /** * Enables or disables checking if the user is allowed to run this app @@ -309,7 +292,6 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) * @warning You need to call this method in your code even if automatic mode is enabled! * * @see identificationType - * @see automaticMode */ - (void) authenticateInstallation; @@ -324,7 +306,8 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) * once needed. * * @see identificationType - * @see automaticMode + * @see authenticateInstallation + * @see validateWithCompletion: * * @param completion Block being executed once identification completed */ @@ -349,7 +332,8 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAppRestrictionEnforcementFrequency) * once needed. * * @see identificationType - * @see automaticMode + * @see authenticateInstallation + * @see identifyWithCompletion: * * @param completion Block being executed once validation completed */ diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index c1ce305da5..c70b01238b 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -64,7 +64,6 @@ static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAut _webpageURL = [NSURL URLWithString:@"https://rink.hockeyapp.net/"]; _identificationType = BITAuthenticatorIdentificationTypeAnonymous; - _automaticMode = YES; _isSetup = NO; _restrictApplicationUsage = NO; _restrictionEnforcementFrequency = BITAuthenticatorAppRestrictionEnforcementOnFirstLaunch; @@ -108,9 +107,6 @@ static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAut } - (void) authenticate { - //when running in manual mode, we don't actually do anything ourselves - if(!self.automaticMode) return; - [self identifyWithCompletion:^(BOOL identified, NSError *error) { if(identified) { if([self needsValidation]) { diff --git a/docs/HowTo-Authenticating-Users-on-iOS-template.md b/docs/HowTo-Authenticating-Users-on-iOS-template.md index 94d3117932..0aa79c7b7f 100644 --- a/docs/HowTo-Authenticating-Users-on-iOS-template.md +++ b/docs/HowTo-Authenticating-Users-on-iOS-template.md @@ -28,11 +28,11 @@ Previous versions of HockeySDK for iOS used the response of the method `UIDevice 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. +The most simple option is 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 +The second option is to implement your own workflow by using - (void) identifyWithCompletion:(void(^)(BOOL identified, NSError *error)) completion; From c73b441a3164167950c3b2d90f025aeba75660f6 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 10 Oct 2013 12:47:20 +0200 Subject: [PATCH 3/3] Add step to setup BITAuthenticator in installation guides --- docs/Guide-Installation-Setup-Advanced-template.md | 2 ++ docs/Guide-Installation-Setup-template.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/Guide-Installation-Setup-Advanced-template.md b/docs/Guide-Installation-Setup-Advanced-template.md index 1084eb0afa..cfa1ed35de 100644 --- a/docs/Guide-Installation-Setup-Advanced-template.md +++ b/docs/Guide-Installation-Setup-Advanced-template.md @@ -129,6 +129,8 @@ The SDK runs on devices with iOS 5.0 or higher. 7. Replace `LIVE_IDENTIFIER` with the app identifier of your release app. We suggest to setup different apps on HockeyApp for your test and production builds. You usually will have way more test versions, but your production version usually has way more crash reports. This helps to keep data separated, getting a better overview and less trouble setting the right app versions downloadable for your beta users. +8. If you want to use the beta distribution feature on iOS 7 or later with In-App Updates, restrict versions to specific users or want to know who is actually testing your app, you need to follow the instructions on our guide [Identify and authenticate users of Ad-Hoc or Enterprise builds](HowTo-Authenticating-Users-on-iOS) + *Note:* The SDK is optimized to defer everything possible to a later time while making sure e.g. crashes on startup can also be caught and each module executes other code with a delay some seconds. This ensures that applicationDidFinishLaunching will process as fast as possible and the SDK will not block the startup sequence resulting in a possible kill by the watchdog process. diff --git a/docs/Guide-Installation-Setup-template.md b/docs/Guide-Installation-Setup-template.md index a5f2ca9b91..6b2a773b2c 100644 --- a/docs/Guide-Installation-Setup-template.md +++ b/docs/Guide-Installation-Setup-template.md @@ -99,6 +99,8 @@ The SDK runs on devices with iOS 5.0 or higher. 7. Replace `LIVE_IDENTIFIER` with the app identifier of your release app. We suggest to setup different apps on HockeyApp for your test and production builds. You usually will have way more test versions, but your production version usually has way more crash reports. This helps to keep data separated, getting a better overview and less trouble setting the right app versions downloadable for your beta users. +8. If you want to use the beta distribution feature on iOS 7 or later with In-App Updates, restrict versions to specific users or want to know who is actually testing your app, you need to follow the instructions on our guide [Identify and authenticate users of Ad-Hoc or Enterprise builds](HowTo-Authenticating-Users-on-iOS) + *Note:* The SDK is optimized to defer everything possible to a later time while making sure e.g. crashes on startup can also be caught and each module executes other code with a delay some seconds. This ensures that applicationDidFinishLaunching will process as fast as possible and the SDK will not block the startup sequence resulting in a possible kill by the watchdog process.