diff --git a/VENTouchLock/VENTouchLock.h b/VENTouchLock/VENTouchLock.h index cf28ed4..dce7831 100644 --- a/VENTouchLock/VENTouchLock.h +++ b/VENTouchLock/VENTouchLock.h @@ -4,6 +4,14 @@ #import "VENTouchLockSplashViewController.h" #import "VENTouchLockOptions.h" +typedef NS_ENUM(NSUInteger, VENTouchLockCompletionType) { + VENTouchLockCompletionTypeUndefined, + VENTouchLockCompletionTypeTouchIDUnlock, + VENTouchLockCompletionTypePasscodeUnlock, + VENTouchLockCompletionTypePasscodeLimitReached, + VENTouchLockCompletionTypeCancel +}; + typedef NS_ENUM(NSUInteger, VENTouchLockTouchIDResponse) { VENTouchLockTouchIDResponseUndefined, VENTouchLockTouchIDResponseSuccess, @@ -19,6 +27,11 @@ typedef NS_ENUM(NSUInteger, VENTouchLockTouchIDResponse) { */ @property (assign, nonatomic, readonly) BOOL locked; +/** + This block is called each time the TouchLock is unlocked or dismissed. + */ +@property (nonatomic, copy) void (^lockCompletion)(VENTouchLockCompletionType); + /** The class of a UIView subclass. When the app is in the background and the TouchLock is locked, an instance of this view, that is diff --git a/VENTouchLock/VENTouchLock.m b/VENTouchLock/VENTouchLock.m index 61a37e8..1070310 100644 --- a/VENTouchLock/VENTouchLock.m +++ b/VENTouchLock/VENTouchLock.m @@ -192,86 +192,52 @@ - (void)lock return; } - __weak typeof(self) weakSelf = self; BOOL fromBackground = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; - UIViewController *displayViewController; - UIView *snapshotView; - void (^displayPresentationCompletionBlock)(); + BOOL shouldUseSplash = ((self.splashViewControllerClass != NULL) + && ([self.splashViewControllerClass isSubclassOfClass:[VENTouchLockSplashViewController class]])); - if (self.splashViewControllerClass != NULL) { - VENTouchLockSplashViewController *splashViewController = [[self.splashViewControllerClass alloc] init]; + self.locked = YES; - void (^didFinishWithResult)(BOOL success, VENTouchLockSplashViewControllerUnlockType unlockType) = splashViewController.didFinishWithResult; + if (shouldUseSplash) { + VENTouchLockSplashViewController *splashViewController = [self splashViewController]; - splashViewController.didFinishWithResult = ^(BOOL success, VENTouchLockSplashViewControllerUnlockType unlockType) { - __strong typeof(self) strongSelf = weakSelf; - dispatch_async(dispatch_get_main_queue(), ^{ - strongSelf.locked = NO; - if (didFinishWithResult) { - didFinishWithResult(success, unlockType); - } - }); - }; + BOOL shouldEmbedInNavigationController = self.options.splashShouldEmbedInNavigationController; + UIViewController *displayViewController; + displayViewController = [self transformViewController:splashViewController byEmbedding:shouldEmbedInNavigationController]; - displayPresentationCompletionBlock = ^{ - [splashViewController showUnlockAnimated:NO]; - }; + if (fromBackground) { + VENTouchLockSplashViewController *snapshotSplashViewController = [[self.splashViewControllerClass alloc] init]; + UIViewController *snapshotDisplayController = [self transformViewController:snapshotSplashViewController byEmbedding:shouldEmbedInNavigationController]; + [snapshotDisplayController loadView]; + [snapshotDisplayController viewDidLoad]; + [self showSnapshotView:snapshotSplashViewController.view]; + } - if ([splashViewController isKindOfClass:[VENTouchLockSplashViewController class]]) { - if (self.options.splashShouldEmbedInNavigationController) { - displayViewController = [splashViewController ventouchlock_embeddedInNavigationControllerWithNavigationBarClass:self.options.navigationBarClass]; - } - else { - displayViewController = splashViewController; - } + [self presentViewControllerOnTop:displayViewController completion:^{ + [splashViewController showUnlockAnimated:NO]; + }]; - if (fromBackground) { - VENTouchLockSplashViewController *snapshotSplashViewController = [[self.splashViewControllerClass alloc] init]; - UIViewController *snapshotDisplayController; - if (self.options.splashShouldEmbedInNavigationController) { - snapshotDisplayController = [snapshotSplashViewController ventouchlock_embeddedInNavigationControllerWithNavigationBarClass:self.options.navigationBarClass]; - } - else { - snapshotDisplayController = snapshotSplashViewController; - } - [snapshotDisplayController loadView]; - [snapshotDisplayController viewDidLoad]; - snapshotView = snapshotDisplayController.view; - } - } } else { - VENTouchLockEnterPasscodeViewController *enterPasscodeViewController = [[VENTouchLockEnterPasscodeViewController alloc] initWithTouchLock:self]; - enterPasscodeViewController.didFinishWithResult = ^(BOOL success) { - __strong typeof(self) strongSelf = weakSelf; - dispatch_async(dispatch_get_main_queue(), ^{ - strongSelf.locked = NO; - }); - }; - if (self.options.passcodeViewControllerShouldEmbedInNavigationController) { - displayViewController = [enterPasscodeViewController ventouchlock_embeddedInNavigationControllerWithNavigationBarClass:self.options.navigationBarClass]; - } else { - displayViewController = enterPasscodeViewController; + if (self.shouldUseTouchID) { + if (!fromBackground) { + [self showTouchID]; + } } + else { + VENTouchLockEnterPasscodeViewController *enterPasscodeViewController = [self enterPasscodeViewController]; - if (fromBackground && self.appSwitchViewClass != NULL) { - snapshotView = [[self.appSwitchViewClass alloc] initWithFrame:CGRectZero]; - } - } + BOOL shouldEmbedInNavigationController = self.options.passcodeViewControllerShouldEmbedInNavigationController; - self.locked = YES; + UIViewController *displayViewController = [self transformViewController:enterPasscodeViewController + byEmbedding:shouldEmbedInNavigationController]; - if (fromBackground && snapshotView) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - UIWindow *mainWindow = [[UIApplication sharedApplication].windows firstObject]; - snapshotView.frame = mainWindow.bounds; - [mainWindow addSubview:snapshotView]; - self.snapshotView = snapshotView; + if (fromBackground && self.appSwitchViewClass != NULL) { + UIView *snapshotView = [[self.appSwitchViewClass alloc] init]; + [self showSnapshotView:snapshotView]; + } + [self presentViewControllerOnTop:displayViewController completion:nil]; + } } - - dispatch_async(dispatch_get_main_queue(), ^{ - UIViewController *rootViewController = [UIViewController ventouchlock_topMostController]; - [rootViewController presentViewController:displayViewController animated:NO completion:displayPresentationCompletionBlock]; - }); } @@ -324,21 +290,10 @@ - (void)applicationWillEnterForeground:(NSNotification *)notification dispatch_async(dispatch_get_main_queue(), ^{ [self.snapshotView removeFromSuperview]; self.snapshotView = nil; + if (self.locked && self.splashViewControllerClass == NULL && [self shouldUseTouchID]) { + [self showTouchID]; + } }); - -} - - -#pragma mark - Internal - -- (NSString *)keychainPasscodeAttemptAccountName -{ - return [self.keychainPasscodeAccount stringByAppendingString:@"_AttemptName"]; -} - -- (NSString *)keypathOfAutolockOptions -{ - return [NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(options)), NSStringFromSelector(@selector(shouldAutoLockOnAppLifeCycleNotifications))]; } @@ -358,7 +313,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(locked))]) { BOOL locked = ((VENTouchLock *)object).locked; - if (locked && self.options.shouldBlurWhenLocked) { + if (locked && self.options.shouldBlurWhenLocked && !self.obscureView) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; UIView *topMostView = [UIViewController ventouchlock_topMostController].view; VENTouchLockBlurView *obscureView = [[VENTouchLockBlurView alloc] initWithFrame:topMostView.bounds blurEffectStyle:self.options.blurEffectStyle]; @@ -373,8 +328,145 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N self.obscureView = nil; }]; } + } } } + + +#pragma mark - Internal Helper Methods + +- (void)showTouchID +{ + [self requestTouchIDWithCompletion:^(VENTouchLockTouchIDResponse response) { + switch (response) { + case VENTouchLockTouchIDResponseSuccess: { + self.locked = NO; + if (self.lockCompletion) { + self.lockCompletion(VENTouchLockCompletionTypePasscodeUnlock); + } + break; + } + case VENTouchLockTouchIDResponseCanceled: { + self.locked = NO; + if (self.lockCompletion) { + self.lockCompletion(VENTouchLockCompletionTypeCancel); + } + break; + } + case VENTouchLockTouchIDResponseUsePasscode: { + VENTouchLockEnterPasscodeViewController *enterPasscodeViewController = [self enterPasscodeViewController]; + BOOL shouldEmbedInNavigationController = self.options.passcodeViewControllerShouldEmbedInNavigationController; + + UIViewController *displayViewController = [self transformViewController:enterPasscodeViewController + byEmbedding:shouldEmbedInNavigationController]; + [self presentViewControllerOnTop:displayViewController completion:nil]; + break; + } + + default: + break; + } + }]; +} + +- (void)showSnapshotView:(UIView *)snapshotView +{ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + UIWindow *mainWindow = [[UIApplication sharedApplication].windows firstObject]; + snapshotView.frame = mainWindow.bounds; + [mainWindow addSubview:snapshotView]; + self.snapshotView = snapshotView; +} + +- (void)presentViewControllerOnTop:(UIViewController *)viewController completion:(void (^)())completion +{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIViewController *rootViewController = [UIViewController ventouchlock_topMostController]; + [rootViewController presentViewController:viewController animated:NO completion:completion]; + }); +} + +- (NSString *)keychainPasscodeAttemptAccountName +{ + return [self.keychainPasscodeAccount stringByAppendingString:@"_AttemptName"]; +} + +- (NSString *)keypathOfAutolockOptions +{ + return [NSString stringWithFormat:@"%@.%@", NSStringFromSelector(@selector(options)), NSStringFromSelector(@selector(shouldAutoLockOnAppLifeCycleNotifications))]; +} + +- (VENTouchLockSplashViewController *)splashViewController +{ + VENTouchLockSplashViewController *splashViewController = [[self.splashViewControllerClass alloc] init]; + + void (^didFinishWithResult)(BOOL success, VENTouchLockSplashViewControllerUnlockType unlockType) = splashViewController.didFinishWithResult; + + __weak typeof(self) weakSelf = self; + splashViewController.didFinishWithResult = ^(BOOL success, VENTouchLockSplashViewControllerUnlockType unlockType) { + __strong typeof(self) strongSelf = weakSelf; + dispatch_async(dispatch_get_main_queue(), ^{ + strongSelf.locked = NO; + if (didFinishWithResult) { + didFinishWithResult(success, unlockType); + } + + VENTouchLockCompletionType lockCompletion; + if (success) { + switch (unlockType) { + case VENTouchLockSplashViewControllerUnlockTypeTouchID: { + lockCompletion = VENTouchLockCompletionTypeTouchIDUnlock; + break; + } + case VENTouchLockSplashViewControllerUnlockTypePasscode: { + lockCompletion = VENTouchLockCompletionTypePasscodeUnlock; + break; + } + default: { + lockCompletion = VENTouchLockCompletionTypeUndefined; + break; + } + } + } else { + lockCompletion = VENTouchLockCompletionTypePasscodeLimitReached; + } + if (strongSelf.lockCompletion) { + self.lockCompletion(lockCompletion); + } + }); + }; + + return splashViewController; +} + +- (VENTouchLockEnterPasscodeViewController *)enterPasscodeViewController +{ + VENTouchLockEnterPasscodeViewController *enterPasscodeViewController = [[VENTouchLockEnterPasscodeViewController alloc] initWithTouchLock:self]; + + __weak typeof(self) weakSelf = self; + enterPasscodeViewController.didFinishWithResult = ^(BOOL success) { + __strong typeof(self) strongSelf = weakSelf; + dispatch_async(dispatch_get_main_queue(), ^{ + strongSelf.locked = NO; + VENTouchLockCompletionType lockCompletion = success ? VENTouchLockCompletionTypePasscodeUnlock : VENTouchLockCompletionTypeCancel; + if (strongSelf.lockCompletion) { + self.lockCompletion(lockCompletion); + } + }); + }; + return enterPasscodeViewController; +} + +- (UIViewController *)transformViewController:(UIViewController *)vc byEmbedding:(BOOL)shouldEmbed +{ + UIViewController *viewController; + if (shouldEmbed) { + viewController = [vc ventouchlock_embeddedInNavigationControllerWithNavigationBarClass:self.options.navigationBarClass]; + } + else { + viewController = vc; + } + return viewController; } @end