From 68c6a85a698415122bc9443cc1e7f584d693cffa Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Mon, 31 Jul 2023 17:44:54 -0700 Subject: [PATCH 01/11] Add a timed loader class GIDTimedLoader encapsulates the logic to handle presenting an activity indicator when fetching the app check token. --- .../UI/GIDActivityIndicatorViewController.h | 1 - .../UI/GIDActivityIndicatorViewController.m | 10 +- GoogleSignIn/Sources/GIDSignIn.m | 48 +++---- .../Sources/GIDTimedLoader/GIDTimedLoader.h | 56 ++++++++ .../Sources/GIDTimedLoader/GIDTimedLoader.m | 123 ++++++++++++++++++ .../project.pbxproj | 12 ++ 6 files changed, 219 insertions(+), 31 deletions(-) create mode 100644 GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h create mode 100644 GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h index f0c77be9..2124d1cc 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h @@ -22,7 +22,6 @@ NS_ASSUME_NONNULL_BEGIN -NS_CLASS_AVAILABLE_IOS(14.0) /// A `UIViewController` presented onscreen to indicate to the user that GSI is performing blocking /// work. @interface GIDActivityIndicatorViewController : UIViewController diff --git a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m index c5615ac2..fa9888e6 100644 --- a/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m +++ b/GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "GIDActivityIndicatorViewController.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -25,7 +25,13 @@ @implementation GIDActivityIndicatorViewController - (void)viewDidLoad { [super viewDidLoad]; - _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge]; + UIActivityIndicatorViewStyle style; + if (@available(iOS 13.0, *)) { + style = UIActivityIndicatorViewStyleLarge; + } else { + style = UIActivityIndicatorViewStyleGray; + } + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style]; self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; [self.activityIndicator startAnimating]; [self.view addSubview:self.activityIndicator]; diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index d2ea2175..36d1b9bd 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -33,6 +33,7 @@ #import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" #import "GoogleSignIn/Sources/GIDAuthStateMigration.h" #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" +#import "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h" @@ -634,35 +635,26 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp if (@available(iOS 14.0, *)) { if (_appCheck) { shouldCallCompletion = NO; - GIDActivityIndicatorViewController *activityVC = - [[GIDActivityIndicatorViewController alloc] init]; - [options.presentingViewController presentViewController:activityVC - animated:true - completion:^{ - // Ensure that the activity indicator shows for at least 1/2 second to prevent "flashing" - // TODO: Re-implement per: https://github.com/google/GoogleSignIn-iOS/issues/329 - dispatch_time_t halfSecond = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC / 2); - dispatch_after(halfSecond, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self->_appCheck getLimitedUseTokenWithCompletion: - ^(id _Nullable token, NSError * _Nullable error) { - if (token) { - additionalParameters[kClientAssertionTypeParameter] = - kClientAssertionTypeParameterValue; - additionalParameters[kClientAssertionParameter] = token.token; - OIDAuthorizationRequest *request = - [self authorizationRequestWithOptions:options - additionalParameters:additionalParameters]; - [activityVC.activityIndicator stopAnimating]; - [activityVC dismissViewControllerAnimated:YES completion:nil]; - completion(request, nil); - return; - } - [activityVC.activityIndicator stopAnimating]; - [activityVC dismissViewControllerAnimated:YES completion:nil]; - completion(nil, error); + UIViewController *presentingVC = options.presentingViewController; + GIDTimedLoader *timedLoader = + [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; + [timedLoader startTiming]; + [self->_appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + [timedLoader stopTimingWithCompletion:^{ + if (token) { + additionalParameters[kClientAssertionTypeParameter] = + kClientAssertionTypeParameterValue; + additionalParameters[kClientAssertionParameter] = token.token; + OIDAuthorizationRequest *request = + [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; + completion(request, nil); return; - }]; - }); + } + completion(nil, error); + return; + }]; }]; } } diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h new file mode 100644 index 00000000..aad9ffe1 --- /dev/null +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Google LLC + * + * 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 +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +extern CFTimeInterval const kGIDTimedLoaderMinAnimationDuration; +extern CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating; + +@class UIViewController; + +NS_ASSUME_NONNULL_BEGIN + +/// A type used to manage the presentation of a load screen for at least +/// `kGIDTimedLoaderMinAnimationDuration` to prevent flashing. +/// +/// `GIDTimedLoader` will also only show its loading screen until +/// `kGIDTimedLoaderMaxDelayBeforeAnimating` has expired. +@interface GIDTimedLoader : NSObject + +/// Created this timed loading controller with the provided presenting view controller, which will +/// be used for presenting hte loading view controller with the activity indicator. +- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController; + +- (instancetype)init NS_UNAVAILABLE; + +/// Tells the controller to start keeping track of loading time. +- (void)startTiming; + +/// Tells the controller to stop keeping track of loading time. +/// +/// @param completion The block to invoke upon successfully stopping. +/// @note Use the completion parameter to, for example, present the UI that should be shown after +/// the work has completed. +- (void)stopTimingWithCompletion:(void (^)(void))completion; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m new file mode 100644 index 00000000..e568f4c3 --- /dev/null +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -0,0 +1,123 @@ +/* + * Copyright 2023 Google LLC + * + * 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 "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h" + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +@import UIKit; +@import CoreMedia; +#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h" +#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h" + +CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0; +CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.5; + +typedef NS_ENUM(NSUInteger, GIDTimedLoaderAnimationStatus) { + GIDTimedLoaderAnimationStatusNotStarted, + GIDTimedLoaderAnimationStatusAnimating, + GIDTimedLoaderAnimationStatusStopped, +}; + +@interface GIDTimedLoader () + +@property(nonatomic, strong) UIViewController *presentingViewController; +@property(nonatomic, strong) GIDActivityIndicatorViewController *loadingViewController; +@property(nonatomic) GIDTimedLoaderAnimationStatus animationStatus; +@property(nonatomic, strong, nullable) NSTimer *loadingTimer; +/// Timestamp representing when the loading view controller was presented and started animating +@property(nonatomic) CFTimeInterval loadingTimeStamp; + +@end + +@implementation GIDTimedLoader + +- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController { + if (self = [super init]) { + _presentingViewController = presentingViewController; + _loadingViewController = [[GIDActivityIndicatorViewController alloc] init]; + _animationStatus = GIDTimedLoaderAnimationStatusNotStarted; + } + return self; +} + +- (void)startTiming { + if (self.animationStatus == GIDTimedLoaderAnimationStatusAnimating) { + return; + } + + self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; + self.loadingTimer = [NSTimer scheduledTimerWithTimeInterval:kGIDTimedLoaderMaxDelayBeforeAnimating + target:self + selector:@selector(presentLoadingViewController) + userInfo:nil + repeats:NO]; +} + +- (void)presentLoadingViewController { + if (self.animationStatus == GIDTimedLoaderAnimationStatusStopped) { + return; + } + self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; + self.loadingTimeStamp = CACurrentMediaTime(); + [self.presentingViewController presentViewController:self.loadingViewController + animated:YES + completion:nil]; +} + +- (void)stopTimingWithCompletion:(void (^)(void))completion { + if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { + return; + } + + if (self.loadingTimer) { + [self.loadingTimer invalidate]; + self.loadingTimer = nil; + } + + dispatch_time_t deadline = [self remainingDurationToAnimate]; + dispatch_after(deadline, dispatch_get_main_queue(), ^{ + NSLog(@"Stopping"); + self.animationStatus = GIDTimedLoaderAnimationStatusStopped; + [self.loadingViewController.activityIndicator stopAnimating]; + [self.loadingViewController dismissViewControllerAnimated:YES completion:nil]; + completion(); + }); +} + +- (dispatch_time_t)remainingDurationToAnimate { + // If we are not animating, then no need to wait + if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { + return 0; + } + + CFTimeInterval now = CACurrentMediaTime(); + CFTimeInterval durationWaited = now - self.loadingTimeStamp; + // If we have already waited for the minimum animation duration, then no need to wait + if (durationWaited >= kGIDTimedLoaderMinAnimationDuration) { + return 0; + } + + CFTimeInterval diff = kGIDTimedLoaderMinAnimationDuration - durationWaited; + NSLog(@"Time to wait: %f", diff); + int64_t diffNanos = diff * NSEC_PER_SEC; + dispatch_time_t timeToWait = dispatch_time(DISPATCH_TIME_NOW, diffNanos); + return timeToWait; +} + +@end + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index 342a46fe..d3470bfb 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; + 73A065642A786E81007BC7FC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 73A065632A786E81007BC7FC /* GoogleService-Info.plist */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */; }; @@ -18,6 +19,8 @@ 1C96B5B2B34E31F1A1CEE95E /* Pods-AppAttestExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttestExample.release.xcconfig"; path = "Target Support Files/Pods-AppAttestExample/Pods-AppAttestExample.release.xcconfig"; sourceTree = ""; }; 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; + 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 73A065632A786E81007BC7FC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 73A464002A1C3B3400BA8528 /* AppAttestExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttestExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -72,6 +75,8 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, + 73A065632A786E81007BC7FC /* GoogleService-Info.plist */, + 73A065612A786D10007BC7FC /* Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); path = AppAttestExample; @@ -154,6 +159,7 @@ buildActionMask = 2147483647; files = ( 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, + 73A065642A786E81007BC7FC /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -339,10 +345,12 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; @@ -359,6 +367,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -373,10 +382,12 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = AppAttestExample/AppAttestExample.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AppAttestExample/Preview Content\""; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AppAttestExample/Info.plist; @@ -393,6 +404,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.google.experimental0.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Experimental App 0 Dev"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From 501f84da116ad4678333f2b47068db07bef6c147 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Wed, 2 Aug 2023 14:20:09 -0700 Subject: [PATCH 02/11] Update GIDTimedLoaader to present loading VC on main queue --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index e568f4c3..cfec5e99 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -73,9 +73,11 @@ - (void)presentLoadingViewController { } self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; self.loadingTimeStamp = CACurrentMediaTime(); - [self.presentingViewController presentViewController:self.loadingViewController - animated:YES - completion:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self.presentingViewController presentViewController:self.loadingViewController + animated:YES + completion:nil]; + }); } - (void)stopTimingWithCompletion:(void (^)(void))completion { From fe4fee06c464b0bfcf386762eecdf26fbe05a348 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Thu, 3 Aug 2023 18:57:59 -0700 Subject: [PATCH 03/11] Only create GIDTimedLoader in GIDSignIn if necessary --- GoogleSignIn/Sources/GIDSignIn.m | 39 +++++++++++-------- .../Sources/GIDTimedLoader/GIDTimedLoader.h | 8 ++++ .../Sources/GIDTimedLoader/GIDTimedLoader.m | 7 ---- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 36d1b9bd..f6eab179 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -180,6 +180,8 @@ @implementation GIDSignIn { BOOL _restarting; // Keychain manager for GTMAppAuth GTMKeychainStore *_keychainStore; + // The class used to manage presenting the loading screen for fetching app check tokens. + GIDTimedLoader *_timedLoader; } #pragma mark - Public methods @@ -636,25 +638,28 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp if (_appCheck) { shouldCallCompletion = NO; UIViewController *presentingVC = options.presentingViewController; - GIDTimedLoader *timedLoader = - [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; - [timedLoader startTiming]; + if (!_timedLoader) { + _timedLoader = [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; + } + if (_timedLoader.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { + [_timedLoader startTiming]; + } [self->_appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, NSError * _Nullable error) { - [timedLoader stopTimingWithCompletion:^{ - if (token) { - additionalParameters[kClientAssertionTypeParameter] = - kClientAssertionTypeParameterValue; - additionalParameters[kClientAssertionParameter] = token.token; - OIDAuthorizationRequest *request = - [self authorizationRequestWithOptions:options - additionalParameters:additionalParameters]; - completion(request, nil); - return; - } - completion(nil, error); - return; - }]; + OIDAuthorizationRequest *request = nil; + if (token) { + additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; + additionalParameters[kClientAssertionParameter] = token.token; + request = [self authorizationRequestWithOptions:options + additionalParameters:additionalParameters]; + } + if (self->_timedLoader.animationStatus == GIDTimedLoaderAnimationStatusAnimating) { + [self->_timedLoader stopTimingWithCompletion:^{ + completion(request, error); + }]; + } else { + completion(request, error); + } }]; } } diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h index aad9ffe1..a7f9d47f 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h @@ -19,6 +19,12 @@ #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +typedef NS_ENUM(NSUInteger, GIDTimedLoaderAnimationStatus) { + GIDTimedLoaderAnimationStatusNotStarted, + GIDTimedLoaderAnimationStatusAnimating, + GIDTimedLoaderAnimationStatusStopped, +}; + extern CFTimeInterval const kGIDTimedLoaderMinAnimationDuration; extern CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating; @@ -49,6 +55,8 @@ NS_ASSUME_NONNULL_BEGIN /// the work has completed. - (void)stopTimingWithCompletion:(void (^)(void))completion; +@property(nonatomic) GIDTimedLoaderAnimationStatus animationStatus; + @end NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index cfec5e99..4883dc15 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -26,17 +26,10 @@ CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0; CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.5; -typedef NS_ENUM(NSUInteger, GIDTimedLoaderAnimationStatus) { - GIDTimedLoaderAnimationStatusNotStarted, - GIDTimedLoaderAnimationStatusAnimating, - GIDTimedLoaderAnimationStatusStopped, -}; - @interface GIDTimedLoader () @property(nonatomic, strong) UIViewController *presentingViewController; @property(nonatomic, strong) GIDActivityIndicatorViewController *loadingViewController; -@property(nonatomic) GIDTimedLoaderAnimationStatus animationStatus; @property(nonatomic, strong, nullable) NSTimer *loadingTimer; /// Timestamp representing when the loading view controller was presented and started animating @property(nonatomic) CFTimeInterval loadingTimeStamp; From 1b5425950f4162fe813e4b957e6a9ba8b179a9a5 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Fri, 4 Aug 2023 15:44:07 -0700 Subject: [PATCH 04/11] Add target check for _timedLoader property --- GoogleSignIn/Sources/GIDSignIn.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index f6eab179..deaf76f8 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -180,8 +180,10 @@ @implementation GIDSignIn { BOOL _restarting; // Keychain manager for GTMAppAuth GTMKeychainStore *_keychainStore; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST // The class used to manage presenting the loading screen for fetching app check tokens. GIDTimedLoader *_timedLoader; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST } #pragma mark - Public methods From d6557bc8a7f065ac38a5788294f59747bb66bd13 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Fri, 4 Aug 2023 16:53:58 -0700 Subject: [PATCH 05/11] Fix issue from rebase --- GoogleSignIn/Sources/GIDSignIn.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index deaf76f8..26a597ef 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -646,8 +646,8 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp if (_timedLoader.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { [_timedLoader startTiming]; } - [self->_appCheck getLimitedUseTokenWithCompletion:^(FIRAppCheckToken * _Nullable token, - NSError * _Nullable error) { + [self->_appCheck getLimitedUseTokenWithCompletion: + ^(id _Nullable token, NSError * _Nullable error) { OIDAuthorizationRequest *request = nil; if (token) { additionalParameters[kClientAssertionTypeParameter] = kClientAssertionTypeParameterValue; From f54f4a7be5a227c38dd404d60a4a37f9ef89bc8c Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Fri, 4 Aug 2023 17:10:32 -0700 Subject: [PATCH 06/11] Remove log statements --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m | 2 -- .../AppAttestExample.xcodeproj/project.pbxproj | 4 ---- 2 files changed, 6 deletions(-) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index 4883dc15..592b4076 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -85,7 +85,6 @@ - (void)stopTimingWithCompletion:(void (^)(void))completion { dispatch_time_t deadline = [self remainingDurationToAnimate]; dispatch_after(deadline, dispatch_get_main_queue(), ^{ - NSLog(@"Stopping"); self.animationStatus = GIDTimedLoaderAnimationStatusStopped; [self.loadingViewController.activityIndicator stopAnimating]; [self.loadingViewController dismissViewControllerAnimated:YES completion:nil]; @@ -107,7 +106,6 @@ - (dispatch_time_t)remainingDurationToAnimate { } CFTimeInterval diff = kGIDTimedLoaderMinAnimationDuration - durationWaited; - NSLog(@"Time to wait: %f", diff); int64_t diffNanos = diff * NSEC_PER_SEC; dispatch_time_t timeToWait = dispatch_time(DISPATCH_TIME_NOW, diffNanos); return timeToWait; diff --git a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj index d3470bfb..15473767 100644 --- a/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj +++ b/Samples/Swift/AppAttestExample/AppAttestExample.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 4D8DB53AAE2F7D0055DCEA7F /* Pods_AppAttestExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F3A930BB86D9E0648046BC /* Pods_AppAttestExample.framework */; }; 738D5F732A26BC3B00A7F11B /* BirthdayLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */; }; - 73A065642A786E81007BC7FC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 73A065632A786E81007BC7FC /* GoogleService-Info.plist */; }; 73A464042A1C3B3400BA8528 /* AppAttestExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */; }; 73A464062A1C3B3400BA8528 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A464052A1C3B3400BA8528 /* ContentView.swift */; }; 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73A4640A2A1C3B3500BA8528 /* Preview Assets.xcassets */; }; @@ -20,7 +19,6 @@ 73443A232A55F56900A4932E /* AppAttestExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppAttestExample.entitlements; sourceTree = ""; }; 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthdayLoader.swift; sourceTree = ""; }; 73A065612A786D10007BC7FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 73A065632A786E81007BC7FC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 73A464002A1C3B3400BA8528 /* AppAttestExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttestExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestExampleApp.swift; sourceTree = ""; }; 73A464052A1C3B3400BA8528 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -75,7 +73,6 @@ 73A464032A1C3B3400BA8528 /* AppAttestExampleApp.swift */, 73A464052A1C3B3400BA8528 /* ContentView.swift */, 738D5F722A26BC3B00A7F11B /* BirthdayLoader.swift */, - 73A065632A786E81007BC7FC /* GoogleService-Info.plist */, 73A065612A786D10007BC7FC /* Info.plist */, 73A464092A1C3B3500BA8528 /* Preview Content */, ); @@ -159,7 +156,6 @@ buildActionMask = 2147483647; files = ( 73A4640B2A1C3B3500BA8528 /* Preview Assets.xcassets in Resources */, - 73A065642A786E81007BC7FC /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 384d386c4fc6de1dc73a1359777a7f393f8a971a Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Fri, 4 Aug 2023 17:14:16 -0700 Subject: [PATCH 07/11] Add clarifying comment about activity indicator animation --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index 592b4076..2ed83246 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -67,6 +67,8 @@ - (void)presentLoadingViewController { self.animationStatus = GIDTimedLoaderAnimationStatusAnimating; self.loadingTimeStamp = CACurrentMediaTime(); dispatch_async(dispatch_get_main_queue(), ^{ + // Since this loading VC may be reused, the activity indicator may have been stopped; restart it + [self.loadingViewController.activityIndicator startAnimating]; [self.presentingViewController presentViewController:self.loadingViewController animated:YES completion:nil]; From 9b5378d0bcedc1d3ebd9243a54d53f946d3534c3 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Tue, 15 Aug 2023 18:04:25 -0700 Subject: [PATCH 08/11] Remove check in GIDSignIn for animation status --- GoogleSignIn/Sources/GIDSignIn.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 26a597ef..22bdc71d 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -643,9 +643,7 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp if (!_timedLoader) { _timedLoader = [[GIDTimedLoader alloc] initWithPresentingViewController:presentingVC]; } - if (_timedLoader.animationStatus != GIDTimedLoaderAnimationStatusAnimating) { - [_timedLoader startTiming]; - } + [_timedLoader startTiming]; [self->_appCheck getLimitedUseTokenWithCompletion: ^(id _Nullable token, NSError * _Nullable error) { OIDAuthorizationRequest *request = nil; From f675864f4f1db0614c68daad43d1c6f22588e263 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Tue, 15 Aug 2023 18:07:05 -0700 Subject: [PATCH 09/11] Add doc comments to GIDTimedLoader.h --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h index a7f9d47f..bc108df0 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h @@ -19,13 +19,19 @@ #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +/// An enumeration detailing the states of the timed loader. typedef NS_ENUM(NSUInteger, GIDTimedLoaderAnimationStatus) { + /// The timed loader has not started. GIDTimedLoaderAnimationStatusNotStarted, + /// The timed loader's activity indicator is animating. GIDTimedLoaderAnimationStatusAnimating, + /// The timed loader's activity indicator has stopped animating. GIDTimedLoaderAnimationStatusStopped, }; +/// The minimum animation duration time for the timed loader's activity indicator. extern CFTimeInterval const kGIDTimedLoaderMinAnimationDuration; +/// The maximum delay to wait before the time loader will display the loading activity indicator. extern CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating; @class UIViewController; From 4d8aa2643da47ad74d567db58efd592bd5a8edaf Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Tue, 15 Aug 2023 18:14:24 -0700 Subject: [PATCH 10/11] Remove reference check for timer in GIDTimedLoader.m --- GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m index 2ed83246..c009dccf 100644 --- a/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m +++ b/GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.m @@ -80,10 +80,8 @@ - (void)stopTimingWithCompletion:(void (^)(void))completion { return; } - if (self.loadingTimer) { - [self.loadingTimer invalidate]; - self.loadingTimer = nil; - } + [self.loadingTimer invalidate]; + self.loadingTimer = nil; dispatch_time_t deadline = [self remainingDurationToAnimate]; dispatch_after(deadline, dispatch_get_main_queue(), ^{ From df8b89b19287b9941275948798e4d73fd52326f0 Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Wed, 16 Aug 2023 16:37:37 -0700 Subject: [PATCH 11/11] Check if _appCheck is configured before using --- GoogleSignIn/Sources/GIDSignIn.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 22bdc71d..bf832d3c 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -637,7 +637,9 @@ - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options comp [self additionalParametersFromOptions:options]; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST if (@available(iOS 14.0, *)) { - if (_appCheck) { + // Only use `_appCheck` (created via singleton `+[GIDSignIn sharedInstance]` call) if + // `-[GIDAppCheck prepareForAppCheckWithCompletion:]` has been called + if ([_appCheck isPrepared]) { shouldCallCompletion = NO; UIViewController *presentingVC = options.presentingViewController; if (!_timedLoader) {