Explorar o código

Replace deprecated API calls (#87)

* Update GIDEMMErrorHandler.m

* Update GIDEMMErrorHandlerTest.m

* Update GIDEMMErrorHandler.m

* Update GIDEMMErrorHandlerTest.m

* Update GIDEMMErrorHandler.m

Uses `-[UIWindow initWithWindowScene:]` for the alert window if available.

* Update GIDEMMErrorHandlerTest.m

Adds a method to test internal `keyWindow` method.

* Adds `nullable` for `keyWindow` method.

Co-authored-by: Peter Andrews <petea@google.com>
Xiangtian Dai %!s(int64=4) %!d(string=hai) anos
pai
achega
8dc6ce955f

+ 55 - 6
GoogleSignIn/Sources/GIDEMMErrorHandler.m

@@ -93,10 +93,23 @@ typedef enum {
   }
   // All UI must happen in the main thread.
   dispatch_async(dispatch_get_main_queue(), ^() {
-    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
-    CGRect keyWindowBounds = CGRectIsEmpty(keyWindow.bounds) ?
+    UIWindow *keyWindow = [self keyWindow];
+    if (!keyWindow) {
+      // Shouldn't happen, just in case.
+      completion();
+      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;
-    UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:keyWindowBounds];
+      alertWindow = [[UIWindow alloc] initWithFrame:keyWindowBounds];
+    }
     alertWindow.backgroundColor = [UIColor clearColor];
     alertWindow.rootViewController = [[UIViewController alloc] init];
     alertWindow.rootViewController.view.backgroundColor = [UIColor clearColor];
@@ -133,6 +146,33 @@ typedef enum {
   return YES;
 }
 
+// This method is exposed to the unit test.
+- (nullable UIWindow *)keyWindow {
+  if (@available(iOS 15, *)) {
+    for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
+      if ([scene isKindOfClass:[UIWindowScene class]] &&
+          scene.activationState == UISceneActivationStateForegroundActive) {
+        return ((UIWindowScene *)scene).keyWindow;
+      }
+    }
+  } else {
+#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;
+}
+
 #pragma mark - Alerts
 
 // Returns an alert controller for device not compliant error.
@@ -176,8 +216,7 @@ typedef enum {
                                               style:UIAlertActionStyleDefault
                                             handler:^(UIAlertAction *action) {
       completion();
-      [[UIApplication sharedApplication]
-          openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
+      [self openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
     }]];
   } else {
     [alert addAction:[UIAlertAction actionWithTitle:[self okayString]
@@ -207,7 +246,7 @@ typedef enum {
                                               style:UIAlertActionStyleDefault
                                             handler:^(UIAlertAction *action) {
       completion();
-      [[UIApplication sharedApplication] openURL:url];
+      [self openURL:url];
     }]];
   } else {
     // If the URL is not provided, simple let user acknowledge the issue. This is not supposed to
@@ -224,6 +263,16 @@ typedef enum {
   return alert;
 }
 
+- (void)openURL:(NSURL *)url {
+  if (@available(iOS 10, *)) {
+    [UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
+  } else {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0
+    [UIApplication.sharedApplication openURL:url];
+#endif  // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0
+  }
+}
+
 #pragma mark - Localization
 
 // The English version of the strings are used as back-up in case the bundle resource is missing

+ 52 - 1
GoogleSignIn/Tests/Unit/GIDEMMErrorHandlerTest.m

@@ -69,6 +69,11 @@ NS_ASSUME_NONNULL_BEGIN
   _isIOS10 = [UIDevice currentDevice].systemVersion.integerValue == 10;
   _keyWindowSet = NO;
   _presentedViewController = nil;
+  UIWindow *fakeKeyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+  [GULSwizzler swizzleClass:[GIDEMMErrorHandler class]
+                   selector:@selector(keyWindow)
+            isClassSelector:NO
+                  withBlock:^() { return fakeKeyWindow; }];
   [GULSwizzler swizzleClass:[UIWindow class]
                    selector:@selector(makeKeyAndVisible)
             isClassSelector:NO
@@ -84,6 +89,9 @@ NS_ASSUME_NONNULL_BEGIN
 }
 
 - (void)tearDown {
+  [GULSwizzler unswizzleClass:[GIDEMMErrorHandler class]
+                     selector:@selector(keyWindow)
+              isClassSelector:NO];
   [GULSwizzler unswizzleClass:[UIWindow class]
                      selector:@selector(makeKeyAndVisible)
               isClassSelector:NO];
@@ -105,7 +113,11 @@ NS_ASSUME_NONNULL_BEGIN
                    selector:@selector(sharedApplication)
             isClassSelector:YES
                   withBlock:^() { return mockApplication; }];
-  [[mockApplication expect] openURL:[NSURL URLWithString:urlString]];
+  if (@available(iOS 10, *)) {
+    [[mockApplication expect] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
+  } else {
+    [[mockApplication expect] openURL:[NSURL URLWithString:urlString]];
+  }
   action();
   [mockApplication verify];
   [GULSwizzler unswizzleClass:[UIApplication class]
@@ -467,6 +479,45 @@ NS_ASSUME_NONNULL_BEGIN
   [self testScreenlockRequiredCancel];
 }
 
+// Verifies that the `keyWindow` internal method works on all OS versions as expected.
+- (void)testKeyWindow {
+  // The original method has been swizzled in `setUp` so get its original implementation to test.
+  typedef id (*KeyWindowSignature)(id, SEL);
+  KeyWindowSignature keyWindowFunction = (KeyWindowSignature)
+      [GULSwizzler originalImplementationForClass:[GIDEMMErrorHandler class]
+                       selector:@selector(keyWindow)
+                isClassSelector:NO];
+  UIWindow *mockKeyWindow = OCMClassMock([UIWindow class]);
+  OCMStub(mockKeyWindow.isKeyWindow).andReturn(YES);
+  UIApplication *mockApplication = OCMClassMock([UIApplication class]);
+  [GULSwizzler swizzleClass:[UIApplication class]
+                   selector:@selector(sharedApplication)
+            isClassSelector:YES
+                  withBlock:^{ return mockApplication; }];
+  if (@available(iOS 15, *)) {
+    UIWindowScene *mockWindowScene = OCMClassMock([UIWindowScene class]);
+    OCMStub(mockApplication.connectedScenes).andReturn(@[mockWindowScene]);
+    OCMStub(mockWindowScene.activationState).andReturn(UISceneActivationStateForegroundActive);
+    OCMStub(mockWindowScene.keyWindow).andReturn(mockKeyWindow);
+  } else {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
+    if (@available(iOS 13, *)) {
+      OCMStub(mockApplication.windows).andReturn(@[mockKeyWindow]);
+    } else {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
+      OCMStub(mockApplication.keyWindow).andReturn(mockKeyWindow);
+#endif  // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
+    }
+#endif  // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
+  }
+  UIWindow *keyWindow =
+      keyWindowFunction([GIDEMMErrorHandler sharedInstance], @selector(keyWindow));
+  XCTAssertEqual(keyWindow, mockKeyWindow);
+  [GULSwizzler unswizzleClass:[UIApplication class]
+                     selector:@selector(sharedApplication)
+              isClassSelector:YES];
+}
+
 #endif
 
 @end