diff options
author | Xiangtian Dai <xiangtian@google.com> | 2017-08-21 21:14:20 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-21 21:14:20 -0700 |
commit | 6e12c501f8dde3057d26149826e82489e114b2a1 (patch) | |
tree | 21145365c0433793ed1f59240db8815812d24fce /Firebase | |
parent | 2c19745fb8f263730a390bb959b00b4209e00916 (diff) |
Forwards app delegate's openURL invocations to `FIRAuth`. (#207)
Diffstat (limited to 'Firebase')
-rw-r--r-- | Firebase/Auth/Source/FIRAuth.m | 4 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAppDelegateProxy.h | 7 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAppDelegateProxy.m | 128 | ||||
-rw-r--r-- | Firebase/Auth/Source/Public/FIRAuth.h | 12 |
4 files changed, 144 insertions, 7 deletions
diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index 33af19e..0a8af0e 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -980,6 +980,10 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; } #endif +- (BOOL)canHandleURL:(NSURL *)url { + return NO; +} + #pragma mark - Internal Methods #if TARGET_OS_IOS diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h index 656e4f2..d974e2c 100644 --- a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h +++ b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h @@ -37,6 +37,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)canHandleNotification:(nonnull NSDictionary *)notification; +/** @fn canHandleURL: + @brief Checks whether the URL can be handled by the receiver, and handles it if so. + @param url The URL in question, which will be consumed if returns @c YES. + @return Whether the URL can be (and already has been) handled by the receiver. + */ +- (BOOL)canHandleURL:(nonnull NSURL *)url; + @end /** @class FIRAuthAppDelegateProxy diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m index f24765a..15151f3 100644 --- a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m +++ b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m @@ -107,6 +107,53 @@ static id noop(id object, SEL cmd, ...) { didReceiveRemoteNotification:notification]; }]; } + SEL openURLOptionsSelector = @selector(application:openURL:options:); + SEL openURLAnnotationSelector = @selector(application:openURL:sourceApplication:annotation:); + SEL handleOpenURLSelector = @selector(application:handleOpenURL:); + if (&UIApplicationOpenURLOptionsAnnotationKey && // the constant is only available on iOS 9+ + ([_appDelegate respondsToSelector:openURLOptionsSelector] || + (![_appDelegate respondsToSelector:openURLAnnotationSelector] && + ![_appDelegate respondsToSelector:handleOpenURLSelector]))) { + // Replace the modern selector which is avaliable on iOS 9 and above because this is the one + // that the client app uses or the client app doesn't use any of them. + [self replaceSelector:openURLOptionsSelector + withBlock:^BOOL(id object, UIApplication *application, NSURL *url, + NSDictionary *options) { + return [weakSelf object:object + selector:openURLOptionsSelector + application:application + openURL:url + options:options]; + }]; + } else if ([_appDelegate respondsToSelector:openURLAnnotationSelector] || + ![_appDelegate respondsToSelector:handleOpenURLSelector]) { + // Replace the longer form of the deprecated selectors on iOS 8 and below because this is the + // one that the client app uses or the client app doesn't use either of the applicable ones. + [self replaceSelector:openURLAnnotationSelector + withBlock:^(id object, UIApplication *application, NSURL *url, + NSString *sourceApplication, id annotation) { + return [weakSelf object:object + selector:openURLAnnotationSelector + application:application + openURL:url + sourceApplication:sourceApplication + annotation:annotation]; + }]; + } else { + // Replace the shorter form of the deprecated selectors on iOS 8 and below because this is + // the only one that the client app uses. + [self replaceSelector:handleOpenURLSelector + withBlock:^(id object, UIApplication *application, NSURL *url) { + return [weakSelf object:object + selector:handleOpenURLSelector + application:application + handleOpenURL:url]; + }]; + } + // Reset the application delegate to clear the system cache that indicates whether each of the + // openURL: methods is implemented on the application delegate. + application.delegate = nil; + application.delegate = _appDelegate; } return self; } @@ -146,7 +193,7 @@ static id noop(id object, SEL cmd, ...) { } } IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation) { + if (originalImplementation && originalImplementation != &noop) { typedef void (*Implmentation)(id, SEL, UIApplication*, NSData *); ((Implmentation)originalImplementation)(object, selector, application, deviceToken); } @@ -166,10 +213,12 @@ static id noop(id object, SEL cmd, ...) { } } IMP originalImplementation = [self originalImplementationForSelector:selector]; - typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *, - void (^)(UIBackgroundFetchResult)); - ((Implmentation)originalImplementation)(object, selector, application, notification, - completionHandler); + if (originalImplementation && originalImplementation != &noop) { + typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *, + void (^)(UIBackgroundFetchResult)); + ((Implmentation)originalImplementation)(object, selector, application, notification, + completionHandler); + } } - (void)object:(id)object @@ -184,12 +233,77 @@ static id noop(id object, SEL cmd, ...) { } } IMP originalImplementation = [self originalImplementationForSelector:selector]; - typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *); - ((Implmentation)originalImplementation)(object, selector, application, notification); + if (originalImplementation && originalImplementation != &noop) { + typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *); + ((Implmentation)originalImplementation)(object, selector, application, notification); + } +} + +- (BOOL)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + openURL:(NSURL *)url + options:(NSDictionary *)options { + if (object == _appDelegate && [self delegateCanHandleURL:url]) { + return YES; + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + if (originalImplementation && originalImplementation != &noop) { + typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *, NSDictionary *); + return ((Implmentation)originalImplementation)(object, selector, application, url, options); + } + return NO; +} + +- (BOOL)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { + if (object == _appDelegate && [self delegateCanHandleURL:url]) { + return YES; + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + if (originalImplementation && originalImplementation != &noop) { + typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *, NSString *, id); + return ((Implmentation)originalImplementation)(object, selector, application, url, + sourceApplication, annotation); + } + return NO; +} + +- (BOOL)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + handleOpenURL:(NSURL *)url { + if (object == _appDelegate && [self delegateCanHandleURL:url]) { + return YES; + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + if (originalImplementation && originalImplementation != &noop) { + typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *); + return ((Implmentation)originalImplementation)(object, selector, application, url); + } + return NO; } #pragma mark - Internal Methods +/** @fn delegateCanHandleURL: + @brief Checks for whether any of the delegates can handle the URL. + @param url The URL in question. + @return Whether any of the delegate can handle the URL. + */ +- (BOOL)delegateCanHandleURL:(NSURL *)url { + for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) { + if ([handler canHandleURL:url]) { + return YES; + }; + } + return NO; +} + /** @fn handlers @brief Gets the list of handlers from `_handlers` safely. */ diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index ad6b635..f2b00ae 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -684,6 +684,18 @@ FIR_SWIFT_NAME(Auth) - (BOOL)canHandleNotification:(NSDictionary *)userInfo; #endif +/** @fn canHandleURL: + @brief Whether the specific URL is handled by @c FIRAuth . + @param url The URL received by the application delegate from any of the openURL method. + @return Whether or the URL is handled. YES means the URL is for Firebase Auth + so the caller should ignore the URL from further processing, and NO means the + the URL is for the app (or another libaray) so the caller should continue handling + this URL as usual. + @remarks If swizzling is disabled, URLs received by the application delegate must be forwarded + to this method for phone number auth to work. + */ +- (BOOL)canHandleURL:(nonnull NSURL *)url; + @end NS_ASSUME_NONNULL_END |