diff options
Diffstat (limited to 'Firebase/Auth')
-rw-r--r-- | Firebase/Auth/FirebaseAuth.podspec | 2 | ||||
-rw-r--r-- | Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m | 9 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h | 38 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m | 14 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthURLPresenter.m | 99 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthWebView.h | 35 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthWebView.m | 86 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthWebViewController.h | 67 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthWebViewController.m | 111 |
9 files changed, 419 insertions, 42 deletions
diff --git a/Firebase/Auth/FirebaseAuth.podspec b/Firebase/Auth/FirebaseAuth.podspec index ad4c54f..57b8a73 100644 --- a/Firebase/Auth/FirebaseAuth.podspec +++ b/Firebase/Auth/FirebaseAuth.podspec @@ -33,6 +33,8 @@ Simplify your iOS development, grow your user base, and monetize more effectivel 'Source/**/FIRAuthDefaultUIDelegate.[mh]', 'Source/**/FIRAuthUIDelegate.h', 'Source/**/FIRAuthURLPresenter.[mh]', + 'Source/**/FIRAuthWebView.[mh]', + 'Source/**/FIRAuthWebViewController.[mh]', 'Source/**/FIRPhoneAuthCredential.[mh]', 'Source/**/FIRPhoneAuthProvider.[mh]' s.public_header_files = 'Source/Public/*.h' diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m index 32b5da0..7587f1b 100644 --- a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m @@ -146,15 +146,16 @@ NSString *const kReCAPTCHAURLStringFormat = @"https://%@/__/auth/handler?%@"; callBackOnMainThread(nil, error); return; } + FIRAuthURLCallbackMatcher callbackMatcher = ^BOOL(NSURL *_Nullable callbackURL) { + return [self isVerifyAppURL:callbackURL]; + }; [_auth.authURLPresenter presentURL:reCAPTCHAURL UIDelegate:UIDelegate - callbackMatcher:^BOOL(NSURL * _Nullable callbackURL) { - return [self isVerifyAppURL:callbackURL]; - } + callbackMatcher:callbackMatcher completion:^(NSURL *_Nullable callbackURL, NSError *_Nullable error) { if (error) { - completion(nil, error); + callBackOnMainThread(nil, error); return; } NSError *reCAPTCHAError; diff --git a/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h b/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h new file mode 100644 index 0000000..f0e5d80 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthUIDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthDefaultUIDelegate : NSObject <FIRAuthUIDelegate> + +/** @fn defaultUIDelegate + @brief Unavailable. Please use @c +defaultUIDelegate: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn defaultUIDelegate + @brief Returns a default FIRAuthUIDelegate object. + @return The default FIRAuthUIDelegate object. + */ ++ (id<FIRAuthUIDelegate>)defaultUIDelegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m b/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m index 118b73c..a00d0e9 100644 --- a/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m +++ b/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m @@ -14,24 +14,20 @@ * limitations under the License. */ -#import <Foundation/Foundation.h> - -#import "FIRAuthUIDelegate.h" +#import "FIRAuthDefaultUIDelegate.h" NS_ASSUME_NONNULL_BEGIN -@interface FIRAuthDefaultUIDelegate : NSObject <FIRAuthUIDelegate> -/** @fn defaultUIDelegate - @brief Unavailable. Please use initWithViewController: - */ -- (instancetype)init NS_UNAVAILABLE; +@interface FIRAuthDefaultUIDelegate () /** @fn initWithViewController: @brief Initializes the instance with a view controller. - @param viewController The view controller as the presenting view controller in @c GOIUIDelegate. + @param viewController The view controller as the presenting view controller in @c + FIRAuthUIDelegate. @return The initialized instance. */ - (instancetype)initWithViewController:(UIViewController *)viewController NS_DESIGNATED_INITIALIZER; + @end @implementation FIRAuthDefaultUIDelegate { diff --git a/Firebase/Auth/Source/FIRAuthURLPresenter.m b/Firebase/Auth/Source/FIRAuthURLPresenter.m index 722326c..d923c8a 100644 --- a/Firebase/Auth/Source/FIRAuthURLPresenter.m +++ b/Firebase/Auth/Source/FIRAuthURLPresenter.m @@ -18,20 +18,15 @@ #import <SafariServices/SafariServices.h> +#import "FIRAuthDefaultUIDelegate.h" #import "FIRAuthErrorUtils.h" +#import "FIRAuthGlobalWorkQueue.h" #import "FIRAuthUIDelegate.h" +#import "FIRAuthWebViewController.h" NS_ASSUME_NONNULL_BEGIN -@interface FIRAuthDefaultUIDelegate : NSObject <FIRAuthUIDelegate> -/** @fn defaultUIDelegate - @brief Returns a default FIRAuthUIDelegate object. - @return The default FIRAuthUIDelegate object. - */ -+ (id<FIRAuthUIDelegate>)defaultUIDelegate; -@end - -@interface FIRAuthURLPresenter () <SFSafariViewControllerDelegate> +@interface FIRAuthURLPresenter () <SFSafariViewControllerDelegate, FIRAuthWebViewDelegate> @end @implementation FIRAuthURLPresenter { @@ -50,6 +45,11 @@ NS_ASSUME_NONNULL_BEGIN */ SFSafariViewController *_Nullable _safariViewController; + /** @var _webViewController + @brief The FIRAuthWebViewController used for the current presentation, if any. + */ + FIRAuthWebViewController *_Nullable _webViewController; + /** @var _UIDelegate @brief The UIDelegate used to present the SFSafariViewController. */ @@ -74,17 +74,20 @@ NS_ASSUME_NONNULL_BEGIN _isPresenting = YES; _callbackMatcher = callbackMatcher; _completion = completion; - _UIDelegate = UIDelegate ?: [FIRAuthDefaultUIDelegate defaultUIDelegate]; - if ([SFSafariViewController class]) { - SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:URL]; - _safariViewController = safariViewController; - _safariViewController.delegate = self; - [_UIDelegate presentViewController:safariViewController animated:YES completion:nil]; - return; - } else { - // TODO: Use web view instead. - [[UIApplication sharedApplication] openURL:URL]; - } + dispatch_async(dispatch_get_main_queue(), ^() { + _UIDelegate = UIDelegate ?: [FIRAuthDefaultUIDelegate defaultUIDelegate]; + if ([SFSafariViewController class]) { + _safariViewController = [[SFSafariViewController alloc] initWithURL:URL]; + _safariViewController.delegate = self; + [_UIDelegate presentViewController:_safariViewController animated:YES completion:nil]; + return; + } else { + _webViewController = [[FIRAuthWebViewController alloc] initWithURL:URL delegate:self]; + UINavigationController *navController = + [[UINavigationController alloc] initWithRootViewController:_webViewController]; + [_UIDelegate presentViewController:navController animated:YES completion:nil]; + } + }); } - (BOOL)canHandleURL:(NSURL *)URL { @@ -98,13 +101,45 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - SFSafariViewControllerDelegate - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller { - if (controller == _safariViewController) { - _safariViewController = nil; - //TODO:Ensure that the SFSafariViewController is actually removed from the screen before - //invoking finishPresentationWithURL:error: - [self finishPresentationWithURL:nil - error:[FIRAuthErrorUtils webContextCancelledErrorWithMessage:nil]]; - } + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + if (controller == _safariViewController) { + _safariViewController = nil; + //TODO:Ensure that the SFSafariViewController is actually removed from the screen before + //invoking finishPresentationWithURL:error: + [self finishPresentationWithURL:nil + error:[FIRAuthErrorUtils webContextCancelledErrorWithMessage:nil]]; + } + }); +} + +#pragma mark - FIRAuthwebViewControllerDelegate + +- (BOOL)webViewController:(FIRAuthWebViewController *)webViewController canHandleURL:(NSURL *)URL { + __block BOOL result = NO; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^() { + if (webViewController == _webViewController) { + result = [self canHandleURL:URL]; + } + }); + return result; +} + +- (void)webViewControllerDidCancel:(FIRAuthWebViewController *)webViewController { + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + if (webViewController == _webViewController) { + [self finishPresentationWithURL:nil + error:[FIRAuthErrorUtils webContextCancelledErrorWithMessage:nil]]; + } + }); +} + +- (void)webViewController:(FIRAuthWebViewController *)webViewController + didFailWithError:(NSError *)error { + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + if (webViewController == _webViewController) { + [self finishPresentationWithURL:nil error:error]; + } + }); } #pragma mark - Private methods @@ -127,8 +162,14 @@ NS_ASSUME_NONNULL_BEGIN }; SFSafariViewController *safariViewController = _safariViewController; _safariViewController = nil; - if (safariViewController) { - [UIDelegate dismissViewControllerAnimated:YES completion:finishBlock]; + FIRAuthWebViewController *webViewController = _webViewController; + _webViewController = nil; + if (safariViewController || webViewController) { + dispatch_async(dispatch_get_main_queue(), ^() { + [UIDelegate dismissViewControllerAnimated:YES completion:^() { + dispatch_async(FIRAuthGlobalWorkQueue(), finishBlock); + }]; + }); } else { finishBlock(); } diff --git a/Firebase/Auth/Source/FIRAuthWebView.h b/Firebase/Auth/Source/FIRAuthWebView.h new file mode 100644 index 0000000..f4a8d1b --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthWebView.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <UIKit/UIKit.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthWebView : UIView + +/** @property webView + * @brief The web view. + */ +@property(nonatomic, weak) UIWebView *webView; + +/** @property spinner + * @brief The spinner that indicates web view loading. + */ +@property(nonatomic, weak) UIActivityIndicatorView *spinner; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthWebView.m b/Firebase/Auth/Source/FIRAuthWebView.m new file mode 100644 index 0000000..80b90f0 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthWebView.m @@ -0,0 +1,86 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRAuthWebView.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRAuthWebView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor whiteColor]; + [self initializeSubviews]; + } + return self; +} + +/** @fn initializeSubviews + @brief Initializes the subviews of this view. + */ +- (void)initializeSubviews { + UIWebView *webView = [self createWebView]; + UIActivityIndicatorView *spinner = [self createSpinner]; + + // The order of the following controls z-order. + [self addSubview:webView]; + [self addSubview:spinner]; + + [self layoutSubviews]; + _webView = webView; + _spinner = spinner; +} + +- (void)layoutSubviews { + CGFloat height = self.bounds.size.height; + CGFloat width = self.bounds.size.width; + _webView.frame = CGRectMake(0, 0, width, height); + _spinner.center = _webView.center; +} + +/** @fn createWebView + @brief Creates a web view to be used by this view. + @return The newly created web view. + */ +- (UIWebView *)createWebView { + UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero]; + // Trickery to make the web view not do weird things (like showing a black background when + // the prompt in the navigation bar animates changes.) + webView.opaque = NO; + webView.backgroundColor = [UIColor clearColor]; + webView.scrollView.opaque = NO; + webView.scrollView.backgroundColor = [UIColor clearColor]; + webView.scrollView.bounces = NO; + webView.scrollView.alwaysBounceVertical = NO; + webView.scrollView.alwaysBounceHorizontal = NO; + return webView; +} + +/** @fn createSpinner + @brief Creates a spinner to be used by this view. + @return The newly created spinner. + */ +- (UIActivityIndicatorView *)createSpinner { + UIActivityIndicatorViewStyle spinnerStyle = UIActivityIndicatorViewStyleGray; + UIActivityIndicatorView *spinner = + [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:spinnerStyle]; + return spinner; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthWebViewController.h b/Firebase/Auth/Source/FIRAuthWebViewController.h new file mode 100644 index 0000000..5c2c042 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthWebViewController.h @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <UIKit/UIKit.h> + +@class FIRAuthWebViewController; + +NS_ASSUME_NONNULL_BEGIN + +@protocol FIRAuthWebViewDelegate <NSObject> + +/** @fn webViewController:canHandleURL: + @brief Determines if a URL should be handled by the delegate. + @param URL The URL to handle. + @return Whether the URL could be handled or not. + */ +- (BOOL)webViewController:(FIRAuthWebViewController *)webViewController canHandleURL:(NSURL *)URL; + +/** @fn webViewControllerDidCancel: + @brief Notifies the delegate that the web view controller is being cancelled by the user. + @param webViewController The web view controller in question. + */ +- (void)webViewControllerDidCancel:(FIRAuthWebViewController *)webViewController; + +/** @fn webViewController:didFailWithError: + @brief Notifies the delegate that the web view controller failed to load a page. + @param webViewController The web view controller in question. + @param error The error that has occurred. + */ +- (void)webViewController:(FIRAuthWebViewController *)webViewController + didFailWithError:(NSError *)error; + +@end + +@interface FIRAuthWebViewController : UIViewController + +/** @fn initWithNibName:bundle: + * @brief Please call initWithURL:delegate: + */ +- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil + bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; + +/** @fn initWithCoder: + * @brief Please call initWithURL:delegate: + */ +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; + +- (instancetype)initWithURL:(NSURL *)URL + delegate:(__weak id<FIRAuthWebViewDelegate>)delegate + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthWebViewController.m b/Firebase/Auth/Source/FIRAuthWebViewController.m new file mode 100644 index 0000000..b9a2473 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthWebViewController.m @@ -0,0 +1,111 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRAuthWebViewController.h" + +#import "FIRAuthWebView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthWebViewController () <UIWebViewDelegate> +@end + +@implementation FIRAuthWebViewController { + /** @var _URL + @brief The initial URL to display. + */ + NSURL *_URL; + + /** @var _delegate + @brief The delegate to call. + */ + __weak id<FIRAuthWebViewDelegate> _delegate; + + /** @var _webView; + @brief The web view instance for easier access. + */ + __weak FIRAuthWebView *_webView; +} + +- (instancetype)initWithURL:(NSURL *)URL + delegate:(__weak id<FIRAuthWebViewDelegate>)delegate { + self = [super initWithNibName:nil bundle:nil]; + if (self) { + _URL = URL; + _delegate = delegate; + } + return self; +} + +#pragma mark - Lifecycle + +- (void)loadView { + FIRAuthWebView *webView = [[FIRAuthWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + webView.webView.delegate = self; + self.view = webView; + _webView = webView; + self.navigationItem.leftBarButtonItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:self + action:@selector(cancel)]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + // Loads the requested URL in the web view. + [_webView.webView loadRequest:[NSURLRequest requestWithURL:_URL]]; +} + +#pragma mark - UI Targets + +- (void)cancel { + [_delegate webViewControllerDidCancel:self]; +} + +#pragma mark - UIWebViewDelegate + +- (BOOL)webView:(UIWebView *)webView + shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType { + return ![_delegate webViewController:self canHandleURL:request.URL]; +} + +- (void)webViewDidStartLoad:(UIWebView *)webView { + // Show & animate the activity indicator. + _webView.spinner.hidden = NO; + [_webView.spinner startAnimating]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + // Hide & stop the activity indicator. + _webView.spinner.hidden = YES; + [_webView.spinner stopAnimating]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + // It's okay for the page to be redirected before it is completely loaded. See b/32028062 . + return; + } + // Forward notification to our delegate. + [self webViewDidFinishLoad:webView]; + [_delegate webViewController:self didFailWithError:error]; +} + +@end + +NS_ASSUME_NONNULL_END |