Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions GoogleSignIn/Sources/GIDEMMErrorHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,38 @@
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#import "GoogleSignIn/Sources/GIDApplication/API/GIDApplication.h"
#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h"

NS_ASSUME_NONNULL_BEGIN

// The handler for displaying EMM-specific errors to users.
/// The handler for displaying EMM-specific errors to users.
@interface GIDEMMErrorHandler : NSObject

// Retrieve the shared instance of this class.
/// Returns the shared instance of this class.
+ (instancetype)sharedInstance;

// Handles EMM-specific errors in the server |response|. |completion| is always called
// exactly once, asynchronously on the main thread, with |YES| if |response| carries an EMM
// error and |NO| otherwise. For an EMM error, a remediation dialog is presented and
// |completion| is called after the user dismisses it — unless another EMM dialog is already
// pending or no dialog can be presented, in which case it is called as soon as possible.
/// Creates a handler that finds the key window and opens URLs through `application`, and shows its
/// alerts through `alertPresenter`.
- (instancetype)initWithApplication:(id<GIDApplication>)application
alertPresenter:(id<GIDEMMAlertPresenter>)alertPresenter
NS_DESIGNATED_INITIALIZER;

/// Creates a handler backed by `UIApplication.sharedApplication` and a
/// `GIDEMMAlertWindowPresenter`.
- (instancetype)init;

/// Returns the application's key window, or nil if there is none.
/// Exposed for the unit tests.
- (nullable UIWindow *)keyWindow;

/// Handles EMM-specific errors in the server |response|. |completion| is always called
/// exactly once, asynchronously on the main thread, with |YES| if |response| carries an EMM
/// error and |NO| otherwise. For an EMM error, a remediation dialog is presented and
/// |completion| is called after the user dismisses it — unless another EMM dialog is already
/// pending or no dialog can be presented, in which case it is called as soon as possible.
- (void)handleErrorFromResponse:(NSDictionary<NSString *, id> *)response
completion:(void (^)(BOOL handled))completion;

Expand Down
72 changes: 25 additions & 47 deletions GoogleSignIn/Sources/GIDEMMErrorHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#import <UIKit/UIKit.h>

#import "GoogleSignIn/Sources/GIDApplication/Implementation/GIDSharedApplication.h"
#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h"
#import "GoogleSignIn/Sources/GIDSignInStrings.h"

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -43,6 +45,10 @@
} ErrorCode;

@implementation GIDEMMErrorHandler {
// Finds the key window and opens URLs.
id<GIDApplication> _application;
// Shows and hides the remediation alert.
id<GIDEMMAlertPresenter> _alertPresenter;
// Whether or not a dialog is pending user interaction.
BOOL _pendingDialog;
}
Expand All @@ -56,6 +62,21 @@ + (instancetype)sharedInstance {
return sharedInstance;
}

- (instancetype)initWithApplication:(id<GIDApplication>)application
alertPresenter:(id<GIDEMMAlertPresenter>)alertPresenter {
self = [super init];
if (self) {
_application = application;
_alertPresenter = alertPresenter;
}
return self;
}

- (instancetype)init {
return [self initWithApplication:[[GIDSharedApplication alloc] init]
alertPresenter:[[GIDEMMAlertWindowPresenter alloc] init]];
}

- (void)handleErrorFromResponse:(NSDictionary<NSString *, id> *)response
completion:(void (^)(BOOL handled))completion {
ErrorCode errorCode = ErrorCodeNone;
Expand Down Expand Up @@ -119,26 +140,8 @@ - (void)handleErrorFromResponse:(NSDictionary<NSString *, id> *)response
completion(YES);
return;
}
UIWindow *alertWindow;
if (@available(iOS 13, *)) {
if (keyWindow.windowScene) {
alertWindow = [[UIWindow alloc] initWithWindowScene:keyWindow.windowScene];
}
}
if (!alertWindow) {
CGRect keyWindowBounds = CGRectIsEmpty(keyWindow.bounds) ?
keyWindow.bounds : [UIScreen mainScreen].bounds;
alertWindow = [[UIWindow alloc] initWithFrame:keyWindowBounds];
}
alertWindow.backgroundColor = [UIColor clearColor];
alertWindow.rootViewController = [[UIViewController alloc] init];
alertWindow.rootViewController.view.backgroundColor = [UIColor clearColor];
alertWindow.windowLevel = UIWindowLevelAlert;
[alertWindow makeKeyAndVisible];
void (^finish)(void) = ^{
alertWindow.hidden = YES;
alertWindow.rootViewController = nil;
[keyWindow makeKeyAndVisible];
[self->_alertPresenter dismissAlert];
clearPendingDialog();
completion(YES);
};
Expand All @@ -157,7 +160,7 @@ - (void)handleErrorFromResponse:(NSDictionary<NSString *, id> *)response
break;
}
if (alert) {
[alertWindow.rootViewController presentViewController:alert animated:YES completion:nil];
[self->_alertPresenter presentAlert:alert overKeyWindow:keyWindow];
} else {
// Should not happen but just in case.
finish();
Expand All @@ -167,32 +170,7 @@ - (void)handleErrorFromResponse:(NSDictionary<NSString *, id> *)response

// This method is exposed to the unit test.
- (nullable UIWindow *)keyWindow {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
if (@available(iOS 15, *)) {
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
if ([scene isKindOfClass:[UIWindowScene class]] &&
scene.activationState == UISceneActivationStateForegroundActive) {
return ((UIWindowScene *)scene).keyWindow;
}
}
} else
#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
if (@available(iOS 13, *)) {
for (UIWindow *window in UIApplication.sharedApplication.windows) {
if (window.isKeyWindow) {
return window;
}
}
} else {
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
return UIApplication.sharedApplication.keyWindow;
#endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
}
#endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
}
return nil;
return [_application keyWindow];
}

#pragma mark - Alerts
Expand Down Expand Up @@ -286,7 +264,7 @@ - (UIAlertController *)appVerificationRequiredAlertWithURL:(nullable NSURL *)url
}

- (void)openURL:(NSURL *)url {
[UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
[_application openURL:url];
}

#pragma mark - Localization
Expand Down
Loading