瀏覽代碼

Merge branch 'main' into GIDGoogleUser-restructure

pinlu 3 年之前
父節點
當前提交
bb2b7d89c3

+ 0 - 0
.github/workflows/ISSUE_TEMPLATES/bug_report.md → .github/ISSUE_TEMPLATE/bug_report.md


+ 1 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1 @@
+blank_issues_enabled: false

+ 0 - 0
.github/workflows/ISSUE_TEMPLATES/feature_request.md → .github/ISSUE_TEMPLATE/feature_request.md


+ 87 - 61
GoogleSignIn/Sources/GIDSignIn.m

@@ -136,6 +136,12 @@ static NSString *const kHostedDomainParameter = @"hd";
 // Minimum time to expiration for a restored access token.
 static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 
+// Info.plist config keys
+static NSString *const kConfigClientIDKey = @"GIDClientID";
+static NSString *const kConfigServerClientIDKey = @"GIDServerClientID";
+static NSString *const kConfigHostedDomainKey = @"GIDHostedDomain";
+static NSString *const kConfigOpenIDRealmKey = @"GIDOpenIDRealm";
+
 // The callback queue used for authentication flow.
 @interface GIDAuthFlow : GIDCallbackQueue
 
@@ -216,20 +222,18 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
       [[OIDIDToken alloc] initWithIDTokenString:authState.lastTokenResponse.idToken];
   GIDProfileData *profileData = [self profileDataWithIDToken:idToken];
 
-  GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState
-                                                     profileData:profileData];
-  [self setCurrentUserWithKVO:user];
+  GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState profileData:profileData];
+  self.currentUser = user;
   return YES;
 }
 
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDUserAuthCompletion)completion {
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                                completion:(nullable GIDUserAuthCompletion)completion {
   GIDSignInInternalOptions *options =
-      [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+      [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                        presentingViewController:presentingViewController
                                                       loginHint:hint
                                                   addScopesFlow:NO
@@ -237,13 +241,12 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDUserAuthCompletion)completion {
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                          additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                                completion:(nullable GIDUserAuthCompletion)completion {
   GIDSignInInternalOptions *options =
-    [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+    [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                      presentingViewController:presentingViewController
                                                     loginHint:hint
                                                 addScopesFlow:NO
@@ -252,13 +255,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                     completion:(nullable GIDUserAuthCompletion)completion {
-  [self signInWithConfiguration:configuration
-       presentingViewController:presentingViewController
-                           hint:nil
-                     completion:completion];
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                completion:(nullable GIDUserAuthCompletion)completion {
+  [self signInWithPresentingViewController:presentingViewController
+                                      hint:nil
+                                completion:completion];
 }
 
 - (void)addScopes:(NSArray<NSString *> *)scopes
@@ -313,12 +314,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 
 #elif TARGET_OS_OSX
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDUserAuthCompletion)completion {
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                        completion:(nullable GIDUserAuthCompletion)completion {
   GIDSignInInternalOptions *options =
-      [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+      [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                                presentingWindow:presentingWindow
                                                       loginHint:hint
                                                   addScopesFlow:NO
@@ -326,22 +326,19 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                     completion:(nullable GIDUserAuthCompletion)completion {
-  [self signInWithConfiguration:configuration
-               presentingWindow:presentingWindow
-                           hint:nil
-                     completion:completion];
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                        completion:(nullable GIDUserAuthCompletion)completion {
+  [self signInWithPresentingWindow:presentingWindow
+                              hint:nil
+                        completion:completion];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDUserAuthCompletion)completion {
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                  additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                        completion:(nullable GIDUserAuthCompletion)completion {
   GIDSignInInternalOptions *options =
-    [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+    [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                              presentingWindow:presentingWindow
                                                     loginHint:hint
                                                 addScopesFlow:NO
@@ -405,9 +402,7 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 - (void)signOut {
   // Clear the current user if there is one.
   if (_currentUser) {
-    [self willChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
-    _currentUser = nil;
-    [self didChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
+    self.currentUser = nil;
   }
   // Remove all state from the keychain.
   [self removeAllKeychainEntries];
@@ -478,6 +473,14 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 - (id)initPrivate {
   self = [super init];
   if (self) {
+    // Get the bundle of the current executable.
+    NSBundle *bundle = NSBundle.mainBundle;
+
+    // If we have a bundle, try to set the active configuration from the bundle's Info.plist.
+    if (bundle) {
+      _configuration = [GIDSignIn configurationFromBundle:bundle];
+    }
+    
     // Check to see if the 3P app is being run for the first time after a fresh install.
     BOOL isFreshInstall = [self isFreshInstall];
 
@@ -515,6 +518,14 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   }
 
   if (options.interactive) {
+    // Ensure that a configuration has been provided.
+    if (!_configuration) {
+      // NOLINTNEXTLINE(google-objc-avoid-throwing-exception)
+      [NSException raise:NSInvalidArgumentException
+                  format:@"No active configuration.  Make sure GIDClientID is set in Info.plist."];
+      return;
+    }
+
     // Explicitly throw exception for missing client ID here. This must come before
     // scheme check because schemes rely on reverse client IDs.
     [self assertValidParameters];
@@ -792,7 +803,7 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
       } else {
         GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState
                                                            profileData:handlerAuthFlow.profileData];
-        [self setCurrentUserWithKVO:user];
+        self.currentUser = user;
       }
     }
   }];
@@ -933,17 +944,6 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   return YES;
 }
 
-#pragma mark - Key-Value Observing
-
-// Override |NSObject(NSKeyValueObservingCustomization)| method in order to provide custom KVO
-// notifications for the |currentUser| property.
-+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
-  if ([key isEqual:NSStringFromSelector(@selector(currentUser))]) {
-    return NO;
-  }
-  return [super automaticallyNotifiesObserversForKey:key];
-}
-
 #pragma mark - Helpers
 
 - (NSError *)errorWithString:(NSString *)errorString code:(GIDSignInErrorCode)code {
@@ -974,10 +974,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 // Assert that the presenting view controller has been set.
 - (void)assertValidPresentingViewController {
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-  if (!_currentOptions.presentingViewController) {
+  if (!_currentOptions.presentingViewController)
 #elif TARGET_OS_OSX
-  if (!_currentOptions.presentingWindow) {
+  if (!_currentOptions.presentingWindow)
 #endif // TARGET_OS_OSX
+  {
     // NOLINTNEXTLINE(google-objc-avoid-throwing-exception)
     [NSException raise:NSInvalidArgumentException
                 format:@"|presentingViewController| must be set."];
@@ -1032,11 +1033,36 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
             imageURL:[NSURL URLWithString:idToken.claims[kBasicProfilePictureKey]]];
 }
 
-// Set currentUser making appropriate KVO calls.
-- (void)setCurrentUserWithKVO:(GIDGoogleUser *_Nullable)user {
-  [self willChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
-  _currentUser = user;
-  [self didChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
+// Try to retrieve a configuration value from an |NSBundle|'s Info.plist for a given key.
++ (nullable NSString *)configValueFromBundle:(NSBundle *)bundle forKey:(NSString *)key {
+  NSString *value;
+  id configValue = [bundle objectForInfoDictionaryKey:key];
+  if ([configValue isKindOfClass:[NSString class]]) {
+    value = configValue;
+  }
+  return value;
+}
+
+// Try to generate a |GIDConfiguration| from an |NSBundle|'s Info.plist.
++ (nullable GIDConfiguration *)configurationFromBundle:(NSBundle *)bundle {
+  GIDConfiguration *configuration;
+
+  // Retrieve any valid config parameters from the bundle's Info.plist.
+  NSString *clientID = [GIDSignIn configValueFromBundle:bundle forKey:kConfigClientIDKey];
+  NSString *serverClientID = [GIDSignIn configValueFromBundle:bundle
+                                                       forKey:kConfigServerClientIDKey];
+  NSString *hostedDomain = [GIDSignIn configValueFromBundle:bundle forKey:kConfigHostedDomainKey];
+  NSString *openIDRealm = [GIDSignIn configValueFromBundle:bundle forKey:kConfigOpenIDRealmKey];
+    
+  // If we have at least a client ID, try to construct a configuration.
+  if (clientID) {
+    configuration = [[GIDConfiguration alloc] initWithClientID:clientID
+                                                 serverClientID:serverClientID
+                                                   hostedDomain:hostedDomain
+                                                    openIDRealm:openIDRealm];
+  }
+  
+  return configuration;
 }
 
 @end

+ 3 - 0
GoogleSignIn/Sources/GIDSignIn_Private.h

@@ -28,6 +28,9 @@ typedef void (^GIDUserAuthCompletion)(GIDUserAuth *_Nullable userAuth, NSError *
 // Private |GIDSignIn| methods that are used internally in this SDK and other Google SDKs.
 @interface GIDSignIn ()
 
+// Redeclare |currentUser| as readwrite for internal use.
+@property(nonatomic, readwrite, nullable) GIDGoogleUser *currentUser;
+
 // Private initializer for |GIDSignIn|.
 - (instancetype)initPrivate;
 

+ 3 - 3
GoogleSignIn/Sources/Public/GoogleSignIn/GIDConfiguration.h

@@ -48,14 +48,14 @@ NS_ASSUME_NONNULL_BEGIN
 /// Initialize a `GIDConfiguration` object with a client ID.
 ///
 /// @param clientID The client ID of the app.
-/// @return An initilized `GIDConfiguration` instance.
+/// @return An initialized `GIDConfiguration` instance.
 - (instancetype)initWithClientID:(NSString *)clientID;
 
 /// Initialize a `GIDConfiguration` object with a client ID and server client ID.
 ///
 /// @param clientID The client ID of the app.
 /// @param serverClientID The server's client ID.
-/// @return An initilized `GIDConfiguration` instance.
+/// @return An initialized `GIDConfiguration` instance.
 - (instancetype)initWithClientID:(NSString *)clientID
                   serverClientID:(nullable NSString *)serverClientID;
 
@@ -65,7 +65,7 @@ NS_ASSUME_NONNULL_BEGIN
 /// @param serverClientID The server's client ID.
 /// @param hostedDomain The Google Apps domain to be used.
 /// @param openIDRealm The OpenID realm to be used.
-/// @return An initilized `GIDConfiguration` instance.
+/// @return An initialized `GIDConfiguration` instance.
 - (instancetype)initWithClientID:(NSString *)clientID
                   serverClientID:(nullable NSString *)serverClientID
                     hostedDomain:(nullable NSString *)hostedDomain

+ 46 - 54
GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h

@@ -66,6 +66,9 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 /// The `GIDGoogleUser` object representing the current user or `nil` if there is no signed-in user.
 @property(nonatomic, readonly, nullable) GIDGoogleUser *currentUser;
 
+/// The active configuration for this instance of `GIDSignIn`.
+@property(nonatomic, nullable) GIDConfiguration *configuration;
+
 /// Unavailable. Use the `sharedInstance` property to instantiate `GIDSignIn`.
 /// :nodoc:
 + (instancetype)new NS_UNAVAILABLE;
@@ -104,68 +107,63 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 - (void)disconnectWithCompletion:(nullable GIDDisconnectCompletion)completion;
 
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-/// Starts an interactive sign-in flow on iOS using the provided configuration.
+/// Starts an interactive sign-in flow on iOS.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on
 ///     iOS 13+.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                              NSError *_Nullable error))completion
     NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions.");
 
-/// Starts an interactive sign-in flow on iOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on iOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on
 ///     iOS 13+.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                                completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                              NSError *_Nullable error))completion
     NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions.");
 
-/// Starts an interactive sign-in flow on iOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on iOS using the provided hint and additional scopes.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
 /// @param additionalScopes An optional array of scopes to request in addition to the basic profile scopes.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion;
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                          additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                                completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                              NSError *_Nullable error))completion;
 
 /// Starts an interactive consent flow on iOS to add scopes to the current user's grants.
 ///
@@ -185,61 +183,55 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
     NS_EXTENSION_UNAVAILABLE("The add scopes flow is not supported in App Extensions."); 
 
 #elif TARGET_OS_OSX
-/// Starts an interactive sign-in flow on macOS using the provided configuration.
+/// Starts an interactive sign-in flow on macOS.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion;
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                        completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                      NSError *_Nullable error))completion;
 
-/// Starts an interactive sign-in flow on macOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on macOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion;
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                        completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                      NSError *_Nullable error))completion;
 
-/// Starts an interactive sign-in flow on macOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on macOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
 /// @param additionalScopes An optional array of scopes to request in addition to the basic profile scopes.
-/// @param completion The block that is called on completion.  This block will be called asynchronously
-///     on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
-                                                   NSError *_Nullable error))completion;
+/// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
+///     be called asynchronously on the main queue.
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                  additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                        completion:(nullable void (^)(GIDUserAuth *_Nullable userAuth,
+                                                      NSError *_Nullable error))completion;
 
 /// Starts an interactive consent flow on macOS to add scopes to the current user's grants.
 ///

+ 16 - 4
GoogleSignIn/Tests/Unit/GIDFakeMainBundle.h

@@ -23,12 +23,11 @@
 @interface GIDFakeMainBundle : NSObject
 
 /**
- * @fn startFakingWithBundleId:clientId:
+ * @fn startFakingWithClientID:
  * @brief Starts faking [NSBundle mainBundle]
- * @param bundleId The fake bundle idenfitier for the app.
- * @param clientId The fake client idenfitier for the app.
+ * @param clientID The fake client idenfitier for the app.
  */
-- (void)startFakingWithBundleId:(NSString *)bundleId clientId:(NSString *)clientId;
+- (void)startFakingWithClientID:(NSString *)clientID;
 
 /**
  * @fn stopFaking
@@ -80,4 +79,17 @@
  */
 - (void)fakeOtherSchemesAndAllSchemes;
 
+/**
+ * @fn fakeWithClientID:serverClientID:hostedDomain:openIDRealm:
+ * @brief Sets values for faked Info.plist params.
+ * @param clientID The fake client idenfitier for the app.
+ * @param serverClientID The fake server client idenfitier for the app.
+ * @param hostedDomain The fake hosted domain for the app.
+ * @param openIDRealm The fake OpenID realm for the app.
+ */
+- (void)fakeWithClientID:(id)clientID
+          serverClientID:(id)serverClientID
+            hostedDomain:(id)hostedDomain
+             openIDRealm:(id)openIDRealm;
+
 @end

+ 42 - 33
GoogleSignIn/Tests/Unit/GIDFakeMainBundle.m

@@ -21,24 +21,39 @@ static NSString *const kCFBundleURLTypesKey = @"CFBundleURLTypes";
 
 static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 
-@implementation GIDFakeMainBundle {
-  // Represents the CFBundleURLTypes of the mocked app bundle's info.plist.
-  __block NSArray *_fakeSupportedSchemes;
+// Info.plist config keys
+static NSString *const kConfigClientIDKey = @"GIDClientID";
+static NSString *const kConfigServerClientIDKey = @"GIDServerClientID";
+static NSString *const kConfigHostedDomainKey = @"GIDHostedDomain";
+static NSString *const kConfigOpenIDRealmKey = @"GIDOpenIDRealm";
 
+@implementation GIDFakeMainBundle {
   NSString *_clientId;
-  NSString *_bundleId;
+
+  // Represents the Info.plist keys to fake.
+  NSArray *_fakedKeys;
+
+  // Represents the values for any Info.plist keys to be faked.
+  NSMutableDictionary *_fakeConfig;
 }
 
-- (void)startFakingWithBundleId:(NSString *)bundleId clientId:(NSString *)clientId {
-  _bundleId = bundleId;
+- (void)startFakingWithClientID:(NSString *)clientId {
   _clientId = clientId;
 
+  _fakedKeys = @[ kCFBundleURLTypesKey,
+                  kConfigClientIDKey,
+                  kConfigServerClientIDKey,
+                  kConfigHostedDomainKey,
+                  kConfigOpenIDRealmKey ];
+  
+  _fakeConfig = [@{ @"GIDClientID" : clientId } mutableCopy];
+
   [GULSwizzler swizzleClass:[NSBundle class]
                    selector:@selector(objectForInfoDictionaryKey:)
             isClassSelector:NO
-                  withBlock:^(id _self, NSString *key) {
-    if ([key isEqual:kCFBundleURLTypesKey]) {
-      return self->_fakeSupportedSchemes;
+                  withBlock:^id(id _self, NSString *key) {
+    if ([self->_fakedKeys containsObject:key]) {
+      return self->_fakeConfig[key];
     } else {
       @throw [NSException exceptionWithName:@"Requested unexpected info.plist key."
                                      reason:nil
@@ -51,7 +66,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
   [GULSwizzler unswizzleClass:[NSBundle class]
                      selector:@selector(objectForInfoDictionaryKey:)
               isClassSelector:NO];
-  _fakeSupportedSchemes = nil;
+  _fakeConfig = nil;
 }
 
 #pragma mark - Utilities
@@ -93,10 +108,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 #pragma mark - URL Schemes
 
 - (void)fakeAllSchemesSupported {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ [self reversedClientId] ]
     }
@@ -104,10 +116,9 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeAllSchemesSupportedAndMerged {
-  _fakeSupportedSchemes = @[
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[
-        _bundleId,
         [self reversedClientId]
       ]
     },
@@ -115,14 +126,9 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeAllSchemesSupportedWithCasesMangled {
-  NSString *caseFlippedBundleId =
-      [self stringByFlippingCasesInString:_bundleId];
   NSString *caseFlippedReverseClientId =
       [self stringByFlippingCasesInString:[self reversedClientId]];
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ caseFlippedBundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ caseFlippedReverseClientId ]
     }
@@ -130,19 +136,15 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeMissingClientIdScheme {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    }
-  ];
+  [self fakeMissingAllSchemes];
 }
 
 - (void)fakeMissingAllSchemes {
-  _fakeSupportedSchemes = nil;
+  _fakeConfig[kCFBundleURLTypesKey] = nil;
 }
 
 - (void)fakeOtherSchemes {
-  _fakeSupportedSchemes = @[
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ @"junk" ]
     }
@@ -150,10 +152,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeOtherSchemesAndAllSchemes {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ @"junk" ]
     },
@@ -163,4 +162,14 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
   ];
 }
 
+- (void)fakeWithClientID:(id)clientID
+          serverClientID:(id)serverClientID
+            hostedDomain:(id)hostedDomain
+             openIDRealm:(id)openIDRealm {
+  _fakeConfig[kConfigClientIDKey] = clientID;
+  _fakeConfig[kConfigServerClientIDKey] = serverClientID;
+  _fakeConfig[kConfigHostedDomainKey] = hostedDomain;
+  _fakeConfig[kConfigOpenIDRealmKey] = openIDRealm;
+}
+
 @end

+ 1 - 2
GoogleSignIn/Tests/Unit/GIDSignInCallbackSchemesTest.m

@@ -18,7 +18,6 @@
 #import "GoogleSignIn/Tests/Unit/GIDFakeMainBundle.h"
 
 static NSString *const kClientId = @"FakeClientID";
-static NSString *const kBundleId = @"FakeBundleID";
 
 @interface GIDSignInCallbackSchemesTest : XCTestCase
 @end
@@ -29,7 +28,7 @@ static NSString *const kBundleId = @"FakeBundleID";
 
 - (void)setUp {
   _fakeMainBundle = [[GIDFakeMainBundle alloc] init];
-  [_fakeMainBundle startFakingWithBundleId:kBundleId clientId:kClientId];
+  [_fakeMainBundle startFakingWithClientID:kClientId];
 }
 
 - (void)tearDown {

+ 76 - 68
GoogleSignIn/Tests/Unit/GIDSignInTest.m

@@ -85,7 +85,7 @@ static NSString * const kFakeIDToken = @"FakeIDToken";
 static NSString * const kClientId = @"FakeClientID";
 static NSString * const kDotReversedClientId = @"FakeClientID";
 static NSString * const kClientId2 = @"FakeClientID2";
-static NSString * const kAppBundleId = @"FakeBundleID";
+static NSString * const kServerClientId = @"FakeServerClientID";
 static NSString * const kLanguage = @"FakeLanguage";
 static NSString * const kScope = @"FakeScope";
 static NSString * const kScope2 = @"FakeScope2";
@@ -153,9 +153,6 @@ static NSString *const kEMMSupport = @"1";
 static NSString *const kGrantedScope = @"grantedScope";
 static NSString *const kNewScope = @"newScope";
 
-/// Unique pointer value for KVO tests.
-static void *kTestObserverContext = &kTestObserverContext;
-
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
 // This category is used to allow the test to swizzle a private method.
 @interface UIViewController (Testing)
@@ -260,9 +257,6 @@ static void *kTestObserverContext = &kTestObserverContext;
   // The saved token request callback.
   OIDTokenCallback _savedTokenCallback;
 
-  // Set of all |GIDSignIn| key paths which were observed to change.
-  NSMutableSet *_changedKeyPaths;
-
   // Status returned by saveAuthorization:toKeychainForName:
   BOOL _saveAuthorizationReturnValue;
 }
@@ -283,7 +277,6 @@ static void *kTestObserverContext = &kTestObserverContext;
   _completionCalled = NO;
   _keychainSaved = NO;
   _keychainRemoved = NO;
-  _changedKeyPaths = [[NSMutableSet alloc] init];
 
   // Mocks
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
@@ -330,7 +323,7 @@ static void *kTestObserverContext = &kTestObserverContext;
   // Fakes
   _fetcherService = [[GIDFakeFetcherService alloc] init];
   _fakeMainBundle = [[GIDFakeMainBundle alloc] init];
-  [_fakeMainBundle startFakingWithBundleId:kAppBundleId clientId:kClientId];
+  [_fakeMainBundle startFakingWithClientID:kClientId];
   [_fakeMainBundle fakeAllSchemesSupported];
 
   // Object under test
@@ -338,7 +331,6 @@ static void *kTestObserverContext = &kTestObserverContext;
                                           forKey:kAppHasRunBeforeKey];
 
   _signIn = [[GIDSignIn alloc] initPrivate];
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId];
   _hint = nil;
 
   __weak GIDSignInTest *weakSelf = self;
@@ -351,11 +343,6 @@ static void *kTestObserverContext = &kTestObserverContext;
     strongSelf->_completionCalled = YES;
     strongSelf->_authError = error;
   };
-
-  [_signIn addObserver:self
-            forKeyPath:NSStringFromSelector(@selector(currentUser))
-               options:0
-               context:kTestObserverContext];
 }
 
 - (void)tearDown {
@@ -375,10 +362,6 @@ static void *kTestObserverContext = &kTestObserverContext;
 
   [_fakeMainBundle stopFaking];
   [super tearDown];
-
-  [_signIn removeObserver:self
-               forKeyPath:NSStringFromSelector(@selector(currentUser))
-                  context:kTestObserverContext];
 }
 
 #pragma mark - Tests
@@ -389,6 +372,46 @@ static void *kTestObserverContext = &kTestObserverContext;
   XCTAssertTrue(signIn1 == signIn2, @"shared instance must be singleton");
 }
 
+- (void)testInitPrivate {
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNotNil(signIn.configuration);
+  XCTAssertEqual(signIn.configuration.clientID, kClientId);
+  XCTAssertNil(signIn.configuration.serverClientID);
+  XCTAssertNil(signIn.configuration.hostedDomain);
+  XCTAssertNil(signIn.configuration.openIDRealm);
+}
+
+- (void)testInitPrivate_noConfig {
+  [_fakeMainBundle fakeWithClientID:nil
+                     serverClientID:nil
+                       hostedDomain:nil
+                        openIDRealm:nil];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNil(signIn.configuration);
+}
+
+- (void)testInitPrivate_fullConfig {
+  [_fakeMainBundle fakeWithClientID:kClientId
+                     serverClientID:kServerClientId
+                       hostedDomain:kFakeHostedDomain
+                        openIDRealm:kOpenIDRealm];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNotNil(signIn.configuration);
+  XCTAssertEqual(signIn.configuration.clientID, kClientId);
+  XCTAssertEqual(signIn.configuration.serverClientID, kServerClientId);
+  XCTAssertEqual(signIn.configuration.hostedDomain, kFakeHostedDomain);
+  XCTAssertEqual(signIn.configuration.openIDRealm, kOpenIDRealm);
+}
+
+- (void)testInitPrivate_invalidConfig {
+  [_fakeMainBundle fakeWithClientID:@[ @"bad", @"config", @"values" ]
+                     serverClientID:nil
+                       hostedDomain:nil
+                        openIDRealm:nil];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNil(signIn.configuration);
+}
+
 - (void)testRestorePreviousSignInNoRefresh_hasPreviousUser {
   [[[_authorization stub] andReturn:_authState] authState];
   [[_authorization expect] setTokenRefreshDelegate:OCMOCK_ANY];
@@ -576,7 +599,11 @@ static void *kTestObserverContext = &kTestObserverContext;
   OCMStub([profile email]).andReturn(kUserEmail);
   
   // Mock for the method `addScopes`.
-  OCMStub([_user configuration]).andReturn(_configuration);
+  GIDConfiguration *configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
+                                                                serverClientID:nil
+                                                                  hostedDomain:nil
+                                                                   openIDRealm:kOpenIDRealm];
+  OCMStub([_user configuration]).andReturn(configuration);
   OCMStub([_user profile]).andReturn(profile);
   OCMStub([_user grantedScopes]).andReturn(@[kGrantedScope]);
 
@@ -612,10 +639,10 @@ static void *kTestObserverContext = &kTestObserverContext;
 }
 
 - (void)testOpenIDRealm {
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
-                                               serverClientID:nil
-                                                 hostedDomain:nil
-                                                  openIDRealm:kOpenIDRealm];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
+                                                      serverClientID:nil
+                                                        hostedDomain:nil
+                                                         openIDRealm:kOpenIDRealm];
 
   [self OAuthLoginWithAddScopesFlow:NO
                           authError:nil
@@ -647,10 +674,10 @@ static void *kTestObserverContext = &kTestObserverContext;
 }
 
 - (void)testOAuthLogin_HostedDomain {
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
-                                               serverClientID:nil
-                                                 hostedDomain:kHostedDomain
-                                                  openIDRealm:nil];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
+                                                      serverClientID:nil
+                                                        hostedDomain:kHostedDomain
+                                                         openIDRealm:nil];
 
   [self OAuthLoginWithAddScopesFlow:NO
                           authError:nil
@@ -725,8 +752,6 @@ static void *kTestObserverContext = &kTestObserverContext;
   [_signIn signOut];
   XCTAssertNil(_signIn.currentUser, @"should not have a current user");
   XCTAssertTrue(_keychainRemoved, @"should remove keychain");
-  XCTAssertTrue([_changedKeyPaths containsObject:NSStringFromSelector(@selector(currentUser))],
-                @"should notify observers that signed in user changed");
 
   OCMVerify([_authorization removeAuthorizationFromKeychainForName:kKeychainName
                                          useDataProtectionKeychain:YES]);
@@ -885,30 +910,28 @@ static void *kTestObserverContext = &kTestObserverContext;
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
 
 
-  XCTAssertThrows([_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                          presentingViewController:_presentingViewController
+  XCTAssertThrows([_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                                  presentingWindow:_presentingWindow
+  XCTAssertThrows([_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                              hint:_hint
-                                        completion:_completion]);
+                                                 hint:_hint
+                                           completion:_completion]);
 }
 
 - (void)testClientIDMissingException {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wnonnull"
-  GIDConfiguration *configuration = [[GIDConfiguration alloc] initWithClientID:nil];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:nil];
 #pragma GCC diagnostic pop
   BOOL threw = NO;
   @try {
-    [_signIn signInWithConfiguration:configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-            presentingViewController:_presentingViewController
+    [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                    presentingWindow:_presentingWindow
+    [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                          completion:nil];
+                             completion:nil];
   } @catch (NSException *exception) {
     threw = YES;
     XCTAssertEqualObjects(exception.description,
@@ -922,14 +945,13 @@ static void *kTestObserverContext = &kTestObserverContext;
   [_fakeMainBundle fakeMissingAllSchemes];
   BOOL threw = NO;
   @try {
-    [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-            presentingViewController:_presentingViewController
+    [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                    presentingWindow:_presentingWindow
+    [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                hint:_hint
-                          completion:_completion];
+                                   hint:_hint
+                             completion:_completion];
   } @catch (NSException *exception) {
     threw = YES;
     XCTAssertEqualObjects(exception.description,
@@ -1250,24 +1272,22 @@ static void *kTestObserverContext = &kTestObserverContext;
               completion:completion];
     } else {
       if (useAdditionalScopes) {
-        [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                presentingViewController:_presentingViewController
+        [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                        presentingWindow:_presentingWindow
+        [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                    hint:_hint
-                        additionalScopes:additionalScopes
-                              completion:completion];
+                                       hint:_hint
+                           additionalScopes:additionalScopes
+                                 completion:completion];
       } else {
-        [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                presentingViewController:_presentingViewController
+        [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                        presentingWindow:_presentingWindow
+        [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                    hint:_hint
-                              completion:completion];
+                                       hint:_hint
+                                 completion:completion];
       }
     }
 
@@ -1434,16 +1454,4 @@ static void *kTestObserverContext = &kTestObserverContext;
   }
 }
 
-
-#pragma mark - Key Value Observing
-
-- (void)observeValueForKeyPath:(NSString *)keyPath
-                      ofObject:(id)object
-                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change
-                       context:(void *)context {
-  if (context == kTestObserverContext && object == _signIn) {
-    [_changedKeyPaths addObject:keyPath];
-  }
-}
-
 @end

+ 3 - 0
Samples/ObjC/SignInSample/SignInSample-Info.plist

@@ -67,5 +67,8 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>589453917038-qaoga89fitj2ukrsq27ko56fimmojac6.apps.googleusercontent.com</string>
 </dict>
 </plist>

+ 3 - 14
Samples/ObjC/SignInSample/Source/SignInViewController.m

@@ -37,11 +37,6 @@ static NSString *const kStyleCellLabel = @"Style";
 // Accessibility Identifiers.
 static NSString *const kCredentialsButtonAccessibilityIdentifier = @"Credentials";
 
-// DO NOT USE THIS CLIENT ID. IT WILL NOT WORK FOR YOUR APP.
-// Please use the client ID created for you by Google.
-static NSString * const kClientID =
-    @"589453917038-qaoga89fitj2ukrsq27ko56fimmojac6.apps.googleusercontent.com";
-
 @implementation SignInViewController {
   // This is an array of arrays, each one corresponding to the cell
   // labels for its respective section.
@@ -58,9 +53,6 @@ static NSString * const kClientID =
 
   // Map that keeps track of which cell corresponds to which DataPickerState.
   NSDictionary *_drilldownCellState;
-
-  // Configuration options for GIDSignIn.
-  GIDConfiguration *_configuration;
 }
 
 #pragma mark - View lifecycle
@@ -97,8 +89,6 @@ static NSString * const kClientID =
   // Make sure the GIDSignInButton class is linked in because references from
   // xib file doesn't count.
   [GIDSignInButton class];
-
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientID];
 }
 
 - (id)initWithNibName:(NSString *)nibNameOrNil
@@ -257,10 +247,9 @@ static NSString * const kClientID =
 #pragma mark - IBActions
 
 - (IBAction)signIn:(id)sender {
-  [GIDSignIn.sharedInstance signInWithConfiguration:_configuration
-                           presentingViewController:self
-                                         completion:^(GIDUserAuth *_Nullable userAuth,
-                                                      NSError *_Nullable error) {
+  [GIDSignIn.sharedInstance signInWithPresentingViewController:self
+                                                    completion:^(GIDGoogleUser *user,
+                                                                 NSError *error) {
     if (error) {
       self->_signInAuthStatus.text =
           [NSString stringWithFormat:@"Status: Authentication error: %@", error];

+ 15 - 4
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj

@@ -553,8 +553,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
 				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -564,6 +566,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -574,8 +577,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
 				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -585,6 +590,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -652,10 +658,12 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "Mac Developer";
+				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = macOS/Info.plist;
@@ -668,6 +676,7 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development macOS";
 				SDKROOT = macosx;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
@@ -681,10 +690,11 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = macOS/Info.plist;
@@ -697,6 +707,7 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development macOS";
 				SDKROOT = macosx;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;

+ 2 - 15
Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift

@@ -19,17 +19,6 @@ import GoogleSignIn
 
 /// An observable class for authenticating via Google.
 final class GoogleSignInAuthenticator: ObservableObject {
-  // TODO: Replace this with your own ID.
-  #if os(iOS)
-  private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
-  #elseif os(macOS)
-  private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
-  #endif
-
-  private lazy var configuration: GIDConfiguration = {
-    return GIDConfiguration(clientID: clientID)
-  }()
-
   private var authViewModel: AuthenticationViewModel
 
   /// Creates an instance of this authenticator.
@@ -47,8 +36,7 @@ final class GoogleSignInAuthenticator: ObservableObject {
       return
     }
 
-    GIDSignIn.sharedInstance.signIn(with: configuration,
-                                    presenting: rootViewController) { userAuth, error in
+    GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) { userAuth, error in
       guard let userAuth = userAuth else {
         print("Error! \(String(describing: error))")
         return
@@ -62,8 +50,7 @@ final class GoogleSignInAuthenticator: ObservableObject {
       return
     }
 
-    GIDSignIn.sharedInstance.signIn(with: configuration,
-                                    presenting: presentingWindow) { userAuth, error in
+    GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) { userAuth, error in
       guard let userAuth = userAuth else {
         print("Error! \(String(describing: error))")
         return

+ 3 - 0
Samples/Swift/DaysUntilBirthday/iOS/Info.plist

@@ -61,5 +61,8 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com</string>
 </dict>
 </plist>

+ 3 - 0
Samples/Swift/DaysUntilBirthday/macOS/Info.plist

@@ -15,5 +15,8 @@
 			</array>
 		</dict>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com</string>
 </dict>
 </plist>