From 6749e9210b3fad81c48f1062e62ede4f47aa21c8 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 22 Oct 2025 14:57:46 -0400 Subject: [PATCH 01/16] Initial GIDSignIn wrapper --- .../FSIGoogleSignInProtocols.h | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h new file mode 100644 index 00000000000..efdcfce51b9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +NS_ASSUME_NONNULL_BEGIN + +/// An abstraction around the GIDSignIn methods used by the plugin, to allow injecting an alternate +/// implementation in unit tests. +/// +/// See GIDSignIn for documentation, as this should always be implemented as a direct passthrough +/// to GIDSignIn. +@protocol FSIGIDSignIn + +@property(nonatomic, nullable) FSISignInConfiguration *configuration; + +- (BOOL)handleURL:(NSURL *)url; + +- (void)restorePreviousSignInWithCompletion:(nullable void (^)(GIDGoogleUser *_Nullable user, + NSError *_Nullable error))completion; + +- (void)signOut; + +- (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion; + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion: + (nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion + NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); + +#elif TARGET_OS_OSX + +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion; + +#endif + +@end + +NS_ASSUME_NONNULL_END From a5f4da5a6025335238de9eff38116d125268a364 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 22 Oct 2025 19:18:43 -0400 Subject: [PATCH 02/16] Implement the passthrough wrapper --- .../FLTGoogleSignInPlugin.m | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index 6608be9b167..ad362bb4691 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -93,6 +93,73 @@ static FSIGoogleSignInErrorCode FSIPigeonErrorCodeForGIDSignInErrorCode(NSIntege } } +@interface FSIGIDSignInWrapper : NSObject + +- (instancetype)init; + +@end + +@implementation FSIGIDSignInWrapper + +- (instancetype)init { + self = [super init]; + if (self) { + _signIn = GIDSignIn.sharedInstance; + } + return self; +} + +- (BOOL)handleURL:(NSURL *)url { + return [_signIn handleURL:url]; +} + +- (void)restorePreviousSignInWithCompletion: + (nullable void (^)(GIDGoogleUser *_Nullable user, NSError *_Nullable error))completion { + [_signIn restorePreviousSignInWithCompletion:completion]; +} + +- (void)signOut { + [_signIn signOut]; +} + +- (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { + [_signIn disconnectWithCompletion:completion]; +} + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion: + (nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion { + [_signIn signInWithPresentingViewController:presentingViewController + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:completion]; +} + +#elif TARGET_OS_OSX +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion { + [_signIn signInWithPresentingWindow:presentingWindow + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:completion]; +} + +#endif + +@end + @interface FLTGoogleSignInPlugin () // The contents of GoogleService-Info.plist, if it exists. From d39fa4e7ce1356d40f79cbde196627b942584994 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 22 Oct 2025 19:22:24 -0400 Subject: [PATCH 03/16] Switch to using the wrapper --- .../google_sign_in_ios/FLTGoogleSignInPlugin.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index ad362bb4691..bca2c48fb54 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -93,6 +93,8 @@ static FSIGoogleSignInErrorCode FSIPigeonErrorCodeForGIDSignInErrorCode(NSIntege } } +#pragma mark - FSIGIDSignIn + @interface FSIGIDSignInWrapper : NSObject - (instancetype)init; @@ -143,6 +145,7 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon } #elif TARGET_OS_OSX + - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes @@ -158,6 +161,8 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow #endif +#pragma mark - + @end @interface FLTGoogleSignInPlugin () @@ -179,17 +184,17 @@ + (void)registerWithRegistrar:(NSObject *)registrar { } - (instancetype)initWithRegistrar:(NSObject *)registrar { - return [self initWithSignIn:GIDSignIn.sharedInstance registrar:registrar]; + return [self initWithSignIn:[[FSIGIDSignInWrapper alloc] init] registrar:registrar]; } -- (instancetype)initWithSignIn:(GIDSignIn *)signIn +- (instancetype)initWithSignIn:(NSObject *)signIn registrar:(NSObject *)registrar { return [self initWithSignIn:signIn registrar:registrar googleServiceProperties:FSILoadGoogleServiceInfo()]; } -- (instancetype)initWithSignIn:(GIDSignIn *)signIn +- (instancetype)initWithSignIn:(NSObject *)signIn registrar:(NSObject *)registrar googleServiceProperties:(nullable NSDictionary *)googleServiceProperties { self = [super init]; From a0803846f3c8105d352c216e0919b67b2f03fdf7 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 08:34:34 -0400 Subject: [PATCH 04/16] Make a test stub --- .../darwin/Tests/GoogleSignInTests.m | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 8daa00077f8..e933aa6f3ee 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -19,6 +19,80 @@ // OCMock library doesn't generate a valid modulemap. #import +@interface TestSignIn : NSObject +// Results to use in completion callbacks. +@property (nonatomic, nullable) GIDGoogleUser *user; +@property (nonatomic, nullable) NSError *error; +@property (nonatomic, nullable) GIDSignInResult *signInResult; + +// Passed parameters. +@property(nonatomic, copy, nullable) NSString *hint; +@property(nonatomic, copy, nullable) NSArray *additionalScopes; +@property(nonatomic, copy, nullable) NSString *nonce; +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +@property(nonatomic, nullable) UIViewController *presentingViewController; +#else +@property(nonatomic, nullable) NSWindow *presentingWindow; +#endif + +@end + +@implementation TestSignIn +@synthesize configuration; + +- (BOOL)handleURL:(NSURL *)url { + return YES; +} + +- (void)restorePreviousSignInWithCompletion:(nullable void (^)(GIDGoogleUser *_Nullable user, + NSError *_Nullable error))completion { + if (completion) { + completion(self.user, self.error); + } +} + +- (void)signOut { +} + +- (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { + if (completion) { + completion(self.error); + } +} + +- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion: + (nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion { + self.presentingViewController = presentingViewController; + self.hint = hint; + self.additionalScopes = additionalScopes; + self.nonce = nonce; + if (completion) { + completion(self.signInResult, self.error); + } +} + +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error))completion { + self.presentingWindow = presentingWindow; + self.hint = hint; + self.additionalScopes = additionalScopes; + self.nonce = nonce; + if (completion) { + completion(self.signInResult, self.error); + } +} + +@end + @interface FLTGoogleSignInPluginTest : XCTestCase @property(strong, nonatomic) NSObject *mockBinaryMessenger; From c236ab483b7e4b48321caff339c9dda067e80e19 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 09:29:47 -0400 Subject: [PATCH 05/16] Update unit tests, fix issues --- .../darwin/Tests/GoogleSignInTests.m | 222 +++++++----------- .../FLTGoogleSignInPlugin.m | 32 +-- .../FLTGoogleSignInPlugin_Test.h | 14 +- .../FSIGoogleSignInProtocols.h | 4 +- 4 files changed, 108 insertions(+), 164 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index e933aa6f3ee..52181dd5a36 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -19,11 +19,16 @@ // OCMock library doesn't generate a valid modulemap. #import -@interface TestSignIn : NSObject +/// Test implementation of @c FSIGIDSignIn. +@interface TestSignIn : NSObject + +// To cause methods to throw an exception. +@property(nonatomic, nullable) NSException *exception; + // Results to use in completion callbacks. -@property (nonatomic, nullable) GIDGoogleUser *user; -@property (nonatomic, nullable) NSError *error; -@property (nonatomic, nullable) GIDSignInResult *signInResult; +@property(nonatomic, nullable) GIDGoogleUser *user; +@property(nonatomic, nullable) NSError *error; +@property(nonatomic, nullable) GIDSignInResult *signInResult; // Passed parameters. @property(nonatomic, copy, nullable) NSString *hint; @@ -35,6 +40,9 @@ @interface TestSignIn : NSObject @property(nonatomic, nullable) NSWindow *presentingWindow; #endif +/// Whether @c signOut was called. +@property(nonatomic) BOOL signOutCalled; + @end @implementation TestSignIn @@ -44,22 +52,30 @@ - (BOOL)handleURL:(NSURL *)url { return YES; } -- (void)restorePreviousSignInWithCompletion:(nullable void (^)(GIDGoogleUser *_Nullable user, - NSError *_Nullable error))completion { +- (void)restorePreviousSignInWithCompletion: + (nullable void (^)(GIDGoogleUser *_Nullable user, NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } if (completion) { - completion(self.user, self.error); + completion(self.user, self.user ? nil : self.error); } } - (void)signOut { + self.signOutCalled = YES; } - (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } if (completion) { completion(self.error); } } +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - (void)signInWithPresentingViewController:(UIViewController *)presentingViewController hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes @@ -67,29 +83,36 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon completion: (nullable void (^)(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } self.presentingViewController = presentingViewController; self.hint = hint; self.additionalScopes = additionalScopes; self.nonce = nonce; if (completion) { - completion(self.signInResult, self.error); + completion(self.signInResult, self.signInResult ? nil : self.error); } } - +#else - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } self.presentingWindow = presentingWindow; self.hint = hint; self.additionalScopes = additionalScopes; self.nonce = nonce; if (completion) { - completion(self.signInResult, self.error); + completion(self.signInResult, self.signInResult ? nil : self.error); } } +#endif @end @@ -98,7 +121,7 @@ @interface FLTGoogleSignInPluginTest : XCTestCase @property(strong, nonatomic) NSObject *mockBinaryMessenger; @property(strong, nonatomic) NSObject *mockPluginRegistrar; @property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; -@property(strong, nonatomic) id mockSignIn; +@property(strong, nonatomic) TestSignIn *fakeSignIn; @property(strong, nonatomic) NSDictionary *googleServiceInfo; @end @@ -110,11 +133,10 @@ - (void)setUp { self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); self.mockPluginRegistrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); - id mockSignIn = OCMClassMock([GIDSignIn class]); - self.mockSignIn = mockSignIn; + self.fakeSignIn = [[TestSignIn alloc] init]; OCMStub(self.mockPluginRegistrar.messenger).andReturn(self.mockBinaryMessenger); - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar]; [FLTGoogleSignInPlugin registerWithRegistrar:self.mockPluginRegistrar]; @@ -129,14 +151,11 @@ - (void)setUp { - (void)testSignOut { FlutterError *error; [self.plugin signOutWithError:&error]; - OCMVerify([self.mockSignIn signOut]); + XCTAssertTrue(self.fakeSignIn.signOutCalled); XCTAssertNil(error); } - (void)testDisconnect { - [(GIDSignIn *)[self.mockSignIn stub] - disconnectWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], nil]]; - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin disconnectWithCompletion:^(FlutterError *error) { XCTAssertNil(error); @@ -149,7 +168,7 @@ - (void)testDisconnect { - (void)testInitNoClientIdNoError { // Init plugin without GoogleService-Info.plist. - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:nil]; @@ -164,7 +183,7 @@ - (void)testInitNoClientIdNoError { } - (void)testInitGoogleServiceInfoPlist { - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = @@ -172,50 +191,38 @@ - (void)testInitGoogleServiceInfoPlist { serverClientId:nil hostedDomain:@"example.com"]; - OCMExpect([self.mockSignIn - setConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *config) { - XCTAssertEqualObjects(config.hostedDomain, @"example.com"); - // Set in example app GoogleService-Info.plist. - XCTAssertEqualObjects( - config.clientID, - @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); - XCTAssertEqualObjects(config.serverClientID, @"YOUR_SERVER_CLIENT_ID"); - return YES; - }]]); - FlutterError *error; [self.plugin configureWithParameters:params error:&error]; XCTAssertNil(error); + XCTAssertEqualObjects(self.fakeSignIn.configuration.hostedDomain, @"example.com"); + // Set in example app GoogleService-Info.plist. + XCTAssertEqualObjects( + self.fakeSignIn.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + XCTAssertEqualObjects(self.fakeSignIn.configuration.serverClientID, @"YOUR_SERVER_CLIENT_ID"); } - (void)testInitDynamicClientIdNullDomain { // Init plugin without GoogleService-Info.plist. - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:nil]; - OCMExpect( - [self.mockSignIn setConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *config) { - XCTAssertEqualObjects(config.hostedDomain, nil); - XCTAssertEqualObjects(config.clientID, @"mockClientId"); - XCTAssertEqualObjects(config.serverClientID, nil); - return YES; - }]]); - FSIPlatformConfigurationParams *params = [FSIPlatformConfigurationParams makeWithClientId:@"mockClientId" serverClientId:nil hostedDomain:nil]; - FlutterError *initializationError; - [self.plugin configureWithParameters:params error:&initializationError]; - XCTAssertNil(initializationError); - - OCMVerifyAll(self.mockSignIn); + FlutterError *error; + [self.plugin configureWithParameters:params error:&error]; + XCTAssertNil(error); + XCTAssertNil(self.fakeSignIn.configuration.hostedDomain); + XCTAssertEqualObjects(self.fakeSignIn.configuration.clientID, @"mockClientId"); + XCTAssertNil(self.fakeSignIn.configuration.serverClientID); } - (void)testInitDynamicServerClientIdNullDomain { - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = @@ -223,20 +230,16 @@ - (void)testInitDynamicServerClientIdNullDomain { serverClientId:@"mockServerClientId" hostedDomain:nil]; - OCMExpect([self.mockSignIn - setConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *config) { - XCTAssertEqualObjects(config.hostedDomain, nil); - // Set in example app GoogleService-Info.plist. - XCTAssertEqualObjects( - config.clientID, - @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); - XCTAssertEqualObjects(config.serverClientID, @"mockServerClientId"); - return YES; - }]]); - - FlutterError *initializationError; - [self.plugin configureWithParameters:params error:&initializationError]; - XCTAssertNil(initializationError); + FlutterError *error; + [self.plugin configureWithParameters:params error:&error]; + XCTAssertNil(error); + XCTAssertNil(self.fakeSignIn.configuration.hostedDomain); + // Set in example app GoogleService-Info.plist. + XCTAssertEqualObjects( + self.fakeSignIn.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + // Overridden by params. + XCTAssertEqualObjects(self.fakeSignIn.configuration.serverClientID, @"mockServerClientId"); } - (void)testInitInfoPlist { @@ -245,21 +248,12 @@ - (void)testInitInfoPlist { serverClientId:nil hostedDomain:@"example.com"]; - OCMExpect([self.mockSignIn - setConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *config) { - XCTAssertEqualObjects(config.hostedDomain, nil); - // Set in example app Info.plist. - XCTAssertEqualObjects( - config.clientID, - @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); - XCTAssertEqualObjects(config.serverClientID, @"YOUR_SERVER_CLIENT_ID"); - return YES; - }]]); - FlutterError *error; - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithRegistrar:self.mockPluginRegistrar]; [self.plugin configureWithParameters:params error:&error]; XCTAssertNil(error); + // No configuration should be set, allowing the SDK to use its default behavior + // (which is to load configuration information from Info.plist). + XCTAssertNil(self.fakeSignIn.configuration); } #pragma mark - restorePreviousSignIn @@ -267,10 +261,7 @@ - (void)testInitInfoPlist { - (void)testSignInSilently { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([mockUser userID]).andReturn(@"mockID"); - - [[self.mockSignIn stub] - restorePreviousSignInWithCompletion:[OCMArg - invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + self.fakeSignIn.user = mockUser; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin restorePreviousSignInWithCompletion:^(FSISignInResult *result, FlutterError *error) { @@ -293,10 +284,7 @@ - (void)testRestorePreviousSignInWithError { NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; - - [[self.mockSignIn stub] - restorePreviousSignInWithCompletion:[OCMArg - invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + self.fakeSignIn.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin restorePreviousSignInWithCompletion:^(FSISignInResult *result, FlutterError *error) { @@ -311,7 +299,7 @@ - (void)testRestorePreviousSignInWithError { #pragma mark - signIn - (void)testSignIn { - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:self.googleServiceInfo]; id mockUser = OCMClassMock([GIDGoogleUser class]); @@ -334,11 +322,7 @@ - (void)testSignIn { OCMStub([mockSignInResult user]).andReturn(mockUser); OCMStub([mockSignInResult serverAuthCode]).andReturn(serverAuthCode); - [self configureMock:[self.mockSignIn expect] - forSignInWithHint:nil - additionalScopes:@[] - nonce:nil - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = mockSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -355,8 +339,6 @@ - (void)testSignIn { [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - - OCMVerifyAll(self.mockSignIn); } - (void)testSignInWithScopeHint { @@ -373,13 +355,7 @@ - (void)testSignInWithScopeHint { OCMStub([mockSignInResult user]).andReturn(mockUser); NSArray *requestedScopes = @[ @"scope1", @"scope2" ]; - [self configureMock:[self.mockSignIn expect] - forSignInWithHint:nil - additionalScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { - return [[NSSet setWithArray:scopes] isEqualToSet:[NSSet setWithArray:requestedScopes]]; - }] - nonce:nil - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = mockSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:requestedScopes @@ -392,7 +368,8 @@ - (void)testSignInWithScopeHint { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerifyAll(self.mockSignIn); + XCTAssertTrue([[NSSet setWithArray:self.fakeSignIn.additionalScopes] + isEqualToSet:[NSSet setWithArray:requestedScopes]]); } - (void)testSignInWithNonce { @@ -409,11 +386,7 @@ - (void)testSignInWithNonce { OCMStub([mockSignInResult user]).andReturn(mockUser); NSString *nonce = @"A nonce"; - [self configureMock:[self.mockSignIn expect] - forSignInWithHint:nil - additionalScopes:OCMOCK_ANY - nonce:nonce - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = mockSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -426,7 +399,7 @@ - (void)testSignInWithNonce { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerifyAll(self.mockSignIn); + XCTAssertEqualObjects(self.fakeSignIn.nonce, nonce); } - (void)testSignInAlreadyGranted { @@ -435,18 +408,12 @@ - (void)testSignInAlreadyGranted { id mockSignInResult = OCMClassMock([GIDSignInResult class]); OCMStub([mockSignInResult user]).andReturn(mockUser); - [self configureMock:[self.mockSignIn stub] - forSignInWithHint:nil - additionalScopes:OCMOCK_ANY - nonce:nil - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = mockSignInResult; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeScopesAlreadyGranted userInfo:nil]; - [self configureMock:mockUser - forAddScopes:OCMOCK_ANY - completion:[OCMArg invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + self.fakeSignIn.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -464,11 +431,7 @@ - (void)testSignInError { NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeCanceled userInfo:nil]; - [self configureMock:[self.mockSignIn stub] - forSignInWithHint:nil - additionalScopes:OCMOCK_ANY - nonce:nil - completion:[OCMArg invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + self.fakeSignIn.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -485,12 +448,9 @@ - (void)testSignInError { } - (void)testSignInExceptionReturnsError { - OCMExpect([self configureMock:self.mockSignIn - forSignInWithHint:OCMOCK_ANY - additionalScopes:OCMOCK_ANY - nonce:nil - completion:OCMOCK_ANY]) - .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); + self.fakeSignIn.exception = [NSException exceptionWithName:@"MockName" + reason:@"MockReason" + userInfo:nil]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -753,25 +713,5 @@ - (void)configureMock:(id)mock [mock addScopes:scopes presentingViewController:OCMOCK_ANY completion:completion]; #endif } -- (void)configureMock:(id)mock - forSignInWithHint:(NSString *)hint - additionalScopes:(NSArray *)additionalScopes - nonce:(nullable NSString *)nonce - completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion { -#if TARGET_OS_OSX - [mock signInWithPresentingWindow:OCMOCK_ANY - hint:hint - additionalScopes:additionalScopes - nonce:nonce - completion:completion]; -#else - [mock signInWithPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] - hint:hint - additionalScopes:additionalScopes - nonce:nonce - completion:completion]; -#endif -} @end diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index bca2c48fb54..9490ffa8ef3 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -95,9 +95,11 @@ static FSIGoogleSignInErrorCode FSIPigeonErrorCodeForGIDSignInErrorCode(NSIntege #pragma mark - FSIGIDSignIn +/// Implementation of @c FSIGIDSignIn that passes through to GIDSignIn. @interface FSIGIDSignInWrapper : NSObject -- (instancetype)init; +/// The underlying GIDSignIn instance. +@property(strong, readonly) GIDSignIn *signIn; @end @@ -112,20 +114,20 @@ - (instancetype)init { } - (BOOL)handleURL:(NSURL *)url { - return [_signIn handleURL:url]; + return [self.signIn handleURL:url]; } - (void)restorePreviousSignInWithCompletion: (nullable void (^)(GIDGoogleUser *_Nullable user, NSError *_Nullable error))completion { - [_signIn restorePreviousSignInWithCompletion:completion]; + [self.signIn restorePreviousSignInWithCompletion:completion]; } - (void)signOut { - [_signIn signOut]; + [self.signIn signOut]; } - (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { - [_signIn disconnectWithCompletion:completion]; + [self.signIn disconnectWithCompletion:completion]; } #if TARGET_OS_IOS || TARGET_OS_MACCATALYST @@ -137,11 +139,11 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon completion: (nullable void (^)(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error))completion { - [_signIn signInWithPresentingViewController:presentingViewController - hint:hint - additionalScopes:additionalScopes - nonce:nonce - completion:completion]; + [self.signIn signInWithPresentingViewController:presentingViewController + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:completion]; } #elif TARGET_OS_OSX @@ -152,11 +154,11 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow nonce:(nullable NSString *)nonce completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error))completion { - [_signIn signInWithPresentingWindow:presentingWindow - hint:hint - additionalScopes:additionalScopes - nonce:nonce - completion:completion]; + [self.signIn signInWithPresentingWindow:presentingWindow + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:completion]; } #endif diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h index 6b5b83d8d53..016bb710837 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h @@ -8,16 +8,16 @@ #import -NS_ASSUME_NONNULL_BEGIN +#import "FSIGoogleSignInProtocols.h" -@class GIDSignIn; +NS_ASSUME_NONNULL_BEGIN /// Methods exposed for unit testing. @interface FLTGoogleSignInPlugin () // Instance used to manage Google Sign In authentication including // sign in, sign out, and requesting additional scopes. -@property(strong, readonly) GIDSignIn *signIn; +@property(strong, readonly) NSObject *signIn; // A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls. @property(nonatomic) NSMutableDictionary *usersByIdentifier; @@ -25,12 +25,12 @@ NS_ASSUME_NONNULL_BEGIN /// Inject @c FlutterPluginRegistrar for testing. - (instancetype)initWithRegistrar:(NSObject *)registrar; -/// Inject @c GIDSignIn for testing. -- (instancetype)initWithSignIn:(GIDSignIn *)signIn +/// Inject @c NSObject for testing. +- (instancetype)initWithSignIn:(NSObject *)signIn registrar:(NSObject *)registrar; -/// Inject @c GIDSignIn and @c googleServiceProperties for testing. -- (instancetype)initWithSignIn:(GIDSignIn *)signIn +/// Inject @c NSObject and @c googleServiceProperties for testing. +- (instancetype)initWithSignIn:(NSObject *)signIn registrar:(NSObject *)registrar googleServiceProperties:(nullable NSDictionary *)googleServiceProperties NS_DESIGNATED_INITIALIZER; diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h index efdcfce51b9..f845520986a 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import + NS_ASSUME_NONNULL_BEGIN /// An abstraction around the GIDSignIn methods used by the plugin, to allow injecting an alternate @@ -11,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN /// to GIDSignIn. @protocol FSIGIDSignIn -@property(nonatomic, nullable) FSISignInConfiguration *configuration; +@property(nonatomic, nullable) GIDConfiguration *configuration; - (BOOL)handleURL:(NSURL *)url; From e2419698c6fa34eb49fa0dcdf61c842e0a47696a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 11:13:12 -0400 Subject: [PATCH 06/16] Add wrappers for the rest of the GID types --- .../darwin/Tests/GoogleSignInTests.m | 18 +- .../FLTGoogleSignInPlugin.m | 284 ++++++++++++++++-- .../FLTGoogleSignInPlugin_Test.h | 3 +- .../FSIGoogleSignInProtocols.h | 101 ++++++- 4 files changed, 366 insertions(+), 40 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 52181dd5a36..1f7fdf67f8c 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -26,9 +26,9 @@ @interface TestSignIn : NSObject @property(nonatomic, nullable) NSException *exception; // Results to use in completion callbacks. -@property(nonatomic, nullable) GIDGoogleUser *user; +@property(nonatomic, nullable) NSObject *user; @property(nonatomic, nullable) NSError *error; -@property(nonatomic, nullable) GIDSignInResult *signInResult; +@property(nonatomic, nullable) NSObject *signInResult; // Passed parameters. @property(nonatomic, copy, nullable) NSString *hint; @@ -53,7 +53,8 @@ - (BOOL)handleURL:(NSURL *)url { } - (void)restorePreviousSignInWithCompletion: - (nullable void (^)(GIDGoogleUser *_Nullable user, NSError *_Nullable error))completion { + (nullable void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion { if (self.exception) { @throw self.exception; } @@ -80,9 +81,9 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion: - (nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion { + completion:(nullable void (^)( + NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { if (self.exception) { @throw self.exception; } @@ -99,8 +100,9 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion { + completion: + (nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { if (self.exception) { @throw self.exception; } diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index 9490ffa8ef3..4f51e637c50 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -7,6 +7,8 @@ #import +#import "FSIGoogleSignInProtocols.h" + // The key within `GoogleService-Info.plist` used to hold the application's // client id. See https://developers.google.com/identity/sign-in/ios/start // for more info. @@ -93,7 +95,8 @@ static FSIGoogleSignInErrorCode FSIPigeonErrorCodeForGIDSignInErrorCode(NSIntege } } -#pragma mark - FSIGIDSignIn +// TODO(stuarmorgan): Replace these with protocol extensions when migrating to Swift. +#pragma mark - Passthrough Wrappers /// Implementation of @c FSIGIDSignIn that passes through to GIDSignIn. @interface FSIGIDSignInWrapper : NSObject @@ -103,6 +106,52 @@ @interface FSIGIDSignInWrapper : NSObject @end +/// A wrapper around the GIDSignInResult methods used by the plugin, to allow injecting an alternate +/// implementation in unit tests. +@interface FSIGIDSignInResultWrapper : NSObject + +- (instancetype)initWithResult:(nullable GIDSignInResult *)result; + +/// The underlying GIDSignInResult instance. +@property(nonatomic, nullable) GIDSignInResult *result; + +@end + +/// A wrapper around the GIDGoogleUser methods used by the plugin, to allow injecting an alternate +/// implementation in unit tests. +@interface FSIGIDGoogleUserWrapper : NSObject + +- (instancetype)initWithUser:(nullable GIDGoogleUser *)user; + +/// The underlying GIDGoogleUser instance. +@property(nonatomic, nullable) GIDGoogleUser *user; + +@end + +/// A wrapper around the GIDProfileData methods used by the plugin, to allow injecting an alternate +/// implementation in unit tests. +@interface FSIGIDProfileDataWrapper : NSObject + +- (instancetype)initWithProfileData:(nullable GIDProfileData *)profileData; + +/// The underlying GIDProfileData instance. +@property(nonatomic, nullable) GIDProfileData *profileData; + +@end + +/// A wrapper around the GIDToken methods used by the plugin, to allow injecting an alternate +/// implementation in unit tests. +@interface FSIGIDTokenWrapper : NSObject + +- (instancetype)initWithToken:(nullable GIDToken *)token; + +/// The underlying GIDToken instance. +@property(nonatomic, nullable) GIDToken *token; + +@end + +#pragma mark - + @implementation FSIGIDSignInWrapper - (instancetype)init { @@ -118,8 +167,13 @@ - (BOOL)handleURL:(NSURL *)url { } - (void)restorePreviousSignInWithCompletion: - (nullable void (^)(GIDGoogleUser *_Nullable user, NSError *_Nullable error))completion { - [self.signIn restorePreviousSignInWithCompletion:completion]; + (nullable void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion { + [self.signIn restorePreviousSignInWithCompletion:^(GIDGoogleUser *user, NSError *error) { + if (completion) { + completion([[FSIGIDGoogleUserWrapper alloc] initWithUser:user], error); + } + }]; } - (void)signOut { @@ -136,14 +190,20 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion: - (nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion { + completion:(nullable void (^)( + NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { [self.signIn signInWithPresentingViewController:presentingViewController hint:hint additionalScopes:additionalScopes nonce:nonce - completion:completion]; + completion:^(GIDSignInResult *result, NSError *error) { + if (completion) { + completion([[FSIGIDSignInResultWrapper alloc] + initWithResult:result], + error); + } + }]; } #elif TARGET_OS_OSX @@ -152,21 +212,195 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, + completion: + (nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { + [self.signIn + signInWithPresentingWindow:presentingWindow + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:^(GIDSignInResult *result, NSError *error) { + if (completion) { + completion([[FSIGIDSignInResultWrapper alloc] initWithResult:result], + error); + } + }]; +} + +#endif + +@end + +#pragma mark - + +@implementation FSIGIDSignInResultWrapper + +- (instancetype)initWithResult:(GIDSignInResult *)result { + if (result == nil) { + return nil; + } + self = [super init]; + if (self) { + _result = result; + } + return self; +} + +- (NSObject *)user { + return [[FSIGIDGoogleUserWrapper alloc] initWithUser:self.result.user]; +} + +- (NSString *)serverAuthCode { + return self.result.serverAuthCode; +} + +@end + +#pragma mark - + +@implementation FSIGIDGoogleUserWrapper + +- (instancetype)initWithUser:(GIDGoogleUser *)user { + if (user == nil) { + return nil; + } + self = [super init]; + if (self) { + _user = user; + } + return self; +} + +- (NSString *)userID { + return self.user.userID; +} + +- (NSObject *)profile { + return [[FSIGIDProfileDataWrapper alloc] initWithProfileData:self.user.profile]; +} + +- (NSArray *)grantedScopes { + return self.user.grantedScopes; +} + +- (NSObject *)accessToken { + return [[FSIGIDTokenWrapper alloc] initWithToken:self.user.accessToken]; +} + +- (NSObject *)refreshToken { + return [[FSIGIDTokenWrapper alloc] initWithToken:self.user.refreshToken]; +} + +- (NSObject *)idToken { + return [[FSIGIDTokenWrapper alloc] initWithToken:self.user.idToken]; +} + +- (void)refreshTokensIfNeededWithCompletion:(void (^)(NSObject *_Nullable user, NSError *_Nullable error))completion { - [self.signIn signInWithPresentingWindow:presentingWindow - hint:hint - additionalScopes:additionalScopes - nonce:nonce - completion:completion]; + [self.user refreshTokensIfNeededWithCompletion:^(GIDGoogleUser *user, NSError *error) { + if (completion) { + completion([[FSIGIDGoogleUserWrapper alloc] initWithUser:user], error); + } + }]; +} + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +- (void)addScopes:(NSArray *)scopes + presentingViewController:(UIViewController *)presentingViewController + completion: + (nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { + [self.user addScopes:scopes + presentingViewController:presentingViewController + completion:^(GIDSignInResult *result, NSError *error) { + if (completion) { + completion([[FSIGIDSignInResultWrapper alloc] initWithResult:result], + error); + } + }]; +} + +#elif TARGET_OS_OSX + +- (void)addScopes:(NSArray *)scopes + presentingWindow:(NSWindow *)presentingWindow + completion:(nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { + [self.user addScopes:scopes + presentingWindow:presentingWindow + completion:^(GIDSignInResult *result, NSError *error) { + if (completion) { + completion([[FSIGIDSignInResultWrapper alloc] initWithResult:result], error); + } + }]; } #endif +@end + +#pragma mark - + +@implementation FSIGIDProfileDataWrapper + +- (instancetype)initWithProfileData:(GIDProfileData *)profileData { + if (profileData == nil) { + return nil; + } + self = [super init]; + if (self) { + _profileData = profileData; + } + return self; +} + +- (NSString *)email { + return self.profileData.email; +} + +- (NSString *)name { + return self.profileData.name; +} + +- (BOOL)hasImage { + return self.profileData.hasImage; +} + +- (NSURL *)imageURLWithDimension:(NSUInteger)dimension { + return [self.profileData imageURLWithDimension:dimension]; +} + +@end + #pragma mark - +@implementation FSIGIDTokenWrapper + +- (instancetype)initWithToken:(GIDToken *)token { + if (token == nil) { + return nil; + } + self = [super init]; + if (self) { + _token = token; + } + return self; +} + +- (NSString *)tokenString { + return self.token.tokenString; +} + +- (NSDate *)expirationDate { + return self.token.expirationDate; +} + @end +#pragma mark - + @interface FLTGoogleSignInPlugin () // The contents of GoogleService-Info.plist, if it exists. @@ -247,7 +481,7 @@ - (void)configureWithParameters:(FSIPlatformConfigurationParams *)params - (void)restorePreviousSignInWithCompletion:(nonnull void (^)(FSISignInResult *_Nullable, FlutterError *_Nullable))completion { __weak typeof(self) weakSelf = self; - [self.signIn restorePreviousSignInWithCompletion:^(GIDGoogleUser *_Nullable user, + [self.signIn restorePreviousSignInWithCompletion:^(NSObject *_Nullable user, NSError *_Nullable error) { [weakSelf handleAuthResultWithUser:user serverAuthCode:nil error:error completion:completion]; }]; @@ -262,7 +496,8 @@ - (void)signInWithScopeHint:(NSArray *)scopeHint [self signInWithHint:nil additionalScopes:scopeHint nonce:nonce - completion:^(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error) { + completion:^(NSObject *_Nullable signInResult, + NSError *_Nullable error) { [weakSelf handleAuthResultWithUser:signInResult.user serverAuthCode:signInResult.serverAuthCode error:error @@ -276,7 +511,7 @@ - (void)signInWithScopeHint:(NSArray *)scopeHint - (void)refreshedAuthorizationTokensForUser:(NSString *)userId completion:(nonnull void (^)(FSISignInResult *_Nullable, FlutterError *_Nullable))completion { - GIDGoogleUser *user = self.usersByIdentifier[userId]; + NSObject *user = self.usersByIdentifier[userId]; if (user == nil) { completion( [FSISignInResult @@ -289,7 +524,7 @@ - (void)refreshedAuthorizationTokensForUser:(NSString *)userId } __weak typeof(self) weakSelf = self; - [user refreshTokensIfNeededWithCompletion:^(GIDGoogleUser *_Nullable refreshedUser, + [user refreshTokensIfNeededWithCompletion:^(NSObject *_Nullable refreshedUser, NSError *_Nullable error) { [weakSelf handleAuthResultWithUser:refreshedUser serverAuthCode:nil @@ -302,7 +537,7 @@ - (void)addScopes:(nonnull NSArray *)scopes forUser:(nonnull NSString *)userId completion: (nonnull void (^)(FSISignInResult *_Nullable, FlutterError *_Nullable))completion { - GIDGoogleUser *user = self.usersByIdentifier[userId]; + NSObject *user = self.usersByIdentifier[userId]; if (user == nil) { completion( [FSISignInResult @@ -318,7 +553,8 @@ - (void)addScopes:(nonnull NSArray *)scopes __weak typeof(self) weakSelf = self; [self addScopes:scopes forGoogleSignInUser:user - completion:^(GIDSignInResult *_Nullable signInResult, NSError *_Nullable error) { + completion:^(NSObject *_Nullable signInResult, + NSError *_Nullable error) { [weakSelf handleAuthResultWithUser:signInResult.user serverAuthCode:signInResult.serverAuthCode error:error @@ -348,7 +584,7 @@ - (void)disconnectWithCompletion:(nonnull void (^)(FlutterError *_Nullable))comp - (void)signInWithHint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion:(void (^)(GIDSignInResult *_Nullable signInResult, + completion:(void (^)(NSObject *_Nullable signInResult, NSError *_Nullable error))completion { #if TARGET_OS_OSX [self.signIn signInWithPresentingWindow:self.registrar.view.window @@ -367,8 +603,8 @@ - (void)signInWithHint:(nullable NSString *)hint // Wraps the iOS and macOS scope addition methods. - (void)addScopes:(nonnull NSArray *)scopes - forGoogleSignInUser:(nonnull GIDGoogleUser *)user - completion:(void (^)(GIDSignInResult *_Nullable signInResult, + forGoogleSignInUser:(nonnull NSObject *)user + completion:(void (^)(NSObject *_Nullable signInResult, NSError *_Nullable error))completion { #if TARGET_OS_OSX [user addScopes:scopes presentingWindow:self.registrar.view.window completion:completion]; @@ -397,7 +633,7 @@ - (GIDConfiguration *)configurationWithClientIdentifier:(nullable NSString *)run openIDRealm:nil]; } -- (void)handleAuthResultWithUser:(nullable GIDGoogleUser *)user +- (void)handleAuthResultWithUser:(nullable NSObject *)user serverAuthCode:(nullable NSString *)serverAuthCode error:(nullable NSError *)error completion:(void (^)(FSISignInResult *_Nullable, @@ -422,7 +658,7 @@ - (void)handleAuthResultWithUser:(nullable GIDGoogleUser *)user } } -- (void)didSignInForUser:(nonnull GIDGoogleUser *)user +- (void)didSignInForUser:(nonnull NSObject *)user withServerAuthCode:(nullable NSString *)serverAuthCode completion:(void (^)(FSISignInResult *_Nullable, FlutterError *_Nullable))completion { self.usersByIdentifier[user.userID] = user; diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h index 016bb710837..586ca1fbdf3 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h @@ -20,7 +20,8 @@ NS_ASSUME_NONNULL_BEGIN @property(strong, readonly) NSObject *signIn; // A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls. -@property(nonatomic) NSMutableDictionary *usersByIdentifier; +@property(nonatomic) + NSMutableDictionary *> *usersByIdentifier; /// Inject @c FlutterPluginRegistrar for testing. - (instancetype)initWithRegistrar:(NSObject *)registrar; diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h index f845520986a..4896c5af603 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h @@ -4,8 +4,93 @@ #import +@protocol FSIGIDGoogleUser; + NS_ASSUME_NONNULL_BEGIN +/// An abstraction around the GIDProfileData methods used by the plugin, to allow injecting an +/// alternate implementation in unit tests. +/// +/// See GIDProfileData for documentation, as this should always be implemented as a direct +/// passthrough to GIDProfileData. +@protocol FSIGIDProfileData + +@property(nonatomic, readonly) NSString *email; +@property(nonatomic, readonly) NSString *name; +@property(nonatomic, readonly) BOOL hasImage; +- (nullable NSURL *)imageURLWithDimension:(NSUInteger)dimension; + +@end + +#pragma mark - + +/// An abstraction around the GIDToken methods used by the plugin, to allow injecting an +/// alternate implementation in unit tests. +/// +/// See GIDToken for documentation, as this should always be implemented as a direct +/// passthrough to GIDToken. +@protocol FSIGIDToken + +@property(nonatomic, readonly) NSString *tokenString; +@property(nonatomic, readonly, nullable) NSDate *expirationDate; + +@end + +#pragma mark - + +/// An abstraction around the GIDSignInResult methods used by the plugin, to allow injecting an +/// alternate implementation in unit tests. +/// +/// See GIDSignInResult for documentation, as this should always be implemented as a direct +/// passthrough to GIDSignInResult. +@protocol FSIGIDSignInResult + +@property(nonatomic, readonly) NSObject *user; +@property(nonatomic, readonly, nullable) NSString *serverAuthCode; + +@end + +#pragma mark - + +/// An abstraction around the GIDGoogleUser methods used by the plugin, to allow injecting an +/// alternate implementation in unit tests. +/// +/// See GIDGoogleUser for documentation, as this should always be implemented as a direct +/// passthrough to GIDGoogleUser. +@protocol FSIGIDGoogleUser + +@property(nonatomic, readonly, nullable) NSString *userID; +@property(nonatomic, readonly, nullable) NSObject *profile; +@property(nonatomic, readonly, nullable) NSArray *grantedScopes; +@property(nonatomic, readonly) NSObject *accessToken; +@property(nonatomic, readonly) NSObject *refreshToken; +@property(nonatomic, readonly, nullable) NSObject *idToken; + +- (void)refreshTokensIfNeededWithCompletion:(void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion; + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +- (void)addScopes:(NSArray *)scopes + presentingViewController:(UIViewController *)presentingViewController + completion: + (nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion + NS_EXTENSION_UNAVAILABLE("The add scopes flow is not supported in App Extensions."); + +#elif TARGET_OS_OSX + +- (void)addScopes:(NSArray *)scopes + presentingWindow:(NSWindow *)presentingWindow + completion:(nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion; + +#endif + +@end + +#pragma mark - + /// An abstraction around the GIDSignIn methods used by the plugin, to allow injecting an alternate /// implementation in unit tests. /// @@ -17,8 +102,9 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)handleURL:(NSURL *)url; -- (void)restorePreviousSignInWithCompletion:(nullable void (^)(GIDGoogleUser *_Nullable user, - NSError *_Nullable error))completion; +- (void)restorePreviousSignInWithCompletion: + (nullable void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion; - (void)signOut; @@ -30,9 +116,9 @@ NS_ASSUME_NONNULL_BEGIN hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion: - (nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion + completion:(nullable void (^)( + NSObject *_Nullable signInResult, + NSError *_Nullable error))completion NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions."); #elif TARGET_OS_OSX @@ -41,8 +127,9 @@ NS_ASSUME_NONNULL_BEGIN hint:(nullable NSString *)hint additionalScopes:(nullable NSArray *)additionalScopes nonce:(nullable NSString *)nonce - completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion; + completion: + (nullable void (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion; #endif From f1017aa8e40160cff0669f08bf98348f8f3ced3d Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 16:32:47 -0400 Subject: [PATCH 07/16] Update tests, and add a missing test --- .../darwin/Tests/GoogleSignInTests.m | 316 ++++++++++++------ .../FSIGoogleSignInProtocols.h | 7 +- 2 files changed, 217 insertions(+), 106 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 1f7fdf67f8c..0826ca7e0da 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -118,6 +118,120 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow @end +/// Test implementation of @c FSIGIDProfileData. +@interface TestProfileData : NSObject +@property(strong, nonatomic) NSString *email; +@property(strong, nonatomic) NSString *name; +/// A URL to return from imageURLWithDimension:. +@property(strong, nonatomic) NSURL *imageURL; +@end + +@implementation TestProfileData + +- (BOOL)hasImage { + return self.imageURL != nil; +} + +- (NSURL *)imageURLWithDimension:(NSUInteger)dimension { + return self.imageURL; +} +@end + +/// Test implementation of @c FSIGIDToken. +@interface TestToken : NSObject +@property(strong, nonatomic) NSString *tokenString; +@property(strong, nonatomic) NSDate *expirationDate; +@end + +@implementation TestToken +@end + +/// Test implementation of @c FSIGIDSignInResult. +@interface TestSignInResult : NSObject +@property(strong, nonatomic) NSObject *user; +@property(strong, nonatomic, nullable) NSString *serverAuthCode; +@end + +@implementation TestSignInResult +@end + +/// Test implementation of @c FSIGIDGoogleUser. +@interface TestGoogleUser : NSObject +@property(strong, nonatomic, nullable) NSString *userID; +@property(strong, nonatomic, nullable) NSObject *profile; +@property(strong, nonatomic, nullable) NSArray *grantedScopes; +@property(strong, nonatomic) NSObject *accessToken; +@property(strong, nonatomic) NSObject *refreshToken; +@property(strong, nonatomic, nullable) NSObject *idToken; + +/// An exception to throw from methods. +@property(nonatomic, nullable) NSException *exception; + +/// The result to return from addScopes:presentingViewController:completion:. +@property(strong, nonatomic, nullable) NSObject *result; + +/// The error to return from methods. +@property(strong, nonatomic, nullable) NSError *error; + +// Values passed as parameters. +@property(strong, nonatomic, nullable) NSArray *requestedScopes; +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +@property(strong, nonatomic, nullable) UIViewController *presentingViewController; +#else +@property(strong, nonatomic, nullable) NSWindow *presentingWindow; +#endif +@end + +@implementation TestGoogleUser + +- (void)refreshTokensIfNeededWithCompletion:(void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } + if (completion) { + completion(self.error ? nil : self, self.error); + } +} + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +- (void)addScopes:(NSArray *)scopes + presentingViewController:(UIViewController *)presentingViewController + completion:(nullable void (^)(NSObject *_Nullable result, + NSError *_Nullable error))completion { + self.requestedScopes = scopes; + self.presentingViewController = presentingViewController; + if (self.exception) { + @throw self.exception; + } + if (completion) { + completion(self.error ? nil : self.result, self.error); + } +} + +#elif TARGET_OS_OSX + +- (void)addScopes:(NSArray *)scopes + presentingWindow:(NSWindow *)presentingWindow + completion:(nullable void (^)(NSObject *_Nullable result, + NSError *_Nullable error))completion { + self.scopes = scopes; + self.presentingWindow = presentingWindow; + if (self.exception) { + @throw self.exception; + } + if (completion) { + completion(self.error ? nil : self.result, self.error); + } +} + +#endif + +@end + +#pragma mark - + @interface FLTGoogleSignInPluginTest : XCTestCase @property(strong, nonatomic) NSObject *mockBinaryMessenger; @@ -261,9 +375,9 @@ - (void)testInitInfoPlist { #pragma mark - restorePreviousSignIn - (void)testSignInSilently { - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(@"mockID"); - self.fakeSignIn.user = mockUser; + TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; + fakeUser.userID = @"mockID"; + self.fakeSignIn.user = fakeUser; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin restorePreviousSignInWithCompletion:^(FSISignInResult *result, FlutterError *error) { @@ -304,27 +418,25 @@ - (void)testSignIn { self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar googleServiceProperties:self.googleServiceInfo]; - id mockUser = OCMClassMock([GIDGoogleUser class]); - id mockUserProfile = OCMClassMock([GIDProfileData class]); - OCMStub([mockUserProfile name]).andReturn(@"mockDisplay"); - OCMStub([mockUserProfile email]).andReturn(@"mock@example.com"); - OCMStub([mockUserProfile hasImage]).andReturn(YES); - OCMStub([mockUserProfile imageURLWithDimension:1337]) - .andReturn([NSURL URLWithString:@"https://example.com/profile.png"]); + TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; + fakeUser.userID = @"mockID"; + TestProfileData *fakeUserProfile = [[TestProfileData alloc] init]; + fakeUserProfile.name = @"mockDisplay"; + fakeUserProfile.email = @"mock@example.com"; + fakeUserProfile.imageURL = [NSURL URLWithString:@"https://example.com/profile.png"]; NSString *accessToken = @"mockAccessToken"; NSString *serverAuthCode = @"mockAuthCode"; - OCMStub([mockUser profile]).andReturn(mockUserProfile); - OCMStub([mockUser userID]).andReturn(@"mockID"); - id mockAccessToken = OCMClassMock([GIDToken class]); - OCMStub([mockAccessToken tokenString]).andReturn(accessToken); - OCMStub([mockUser accessToken]).andReturn(mockAccessToken); + fakeUser.profile = fakeUserProfile; + TestToken *fakeAccessToken = [[TestToken alloc] init]; + fakeAccessToken.tokenString = accessToken; + fakeUser.accessToken = fakeAccessToken; - id mockSignInResult = OCMClassMock([GIDSignInResult class]); - OCMStub([mockSignInResult user]).andReturn(mockUser); - OCMStub([mockSignInResult serverAuthCode]).andReturn(serverAuthCode); + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeUser; + fakeSignInResult.serverAuthCode = serverAuthCode; - self.fakeSignIn.signInResult = mockSignInResult; + self.fakeSignIn.signInResult = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -351,13 +463,13 @@ - (void)testSignInWithScopeHint { error:&initializationError]; XCTAssertNil(initializationError); - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(@"mockID"); - id mockSignInResult = OCMClassMock([GIDSignInResult class]); - OCMStub([mockSignInResult user]).andReturn(mockUser); + TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; + fakeUser.userID = @"mockID"; + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeUser; NSArray *requestedScopes = @[ @"scope1", @"scope2" ]; - self.fakeSignIn.signInResult = mockSignInResult; + self.fakeSignIn.signInResult = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:requestedScopes @@ -382,13 +494,13 @@ - (void)testSignInWithNonce { error:&initializationError]; XCTAssertNil(initializationError); - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(@"mockID"); - id mockSignInResult = OCMClassMock([GIDSignInResult class]); - OCMStub([mockSignInResult user]).andReturn(mockUser); + TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; + fakeUser.userID = @"mockID"; + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeUser; NSString *nonce = @"A nonce"; - self.fakeSignIn.signInResult = mockSignInResult; + self.fakeSignIn.signInResult = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -405,12 +517,12 @@ - (void)testSignInWithNonce { } - (void)testSignInAlreadyGranted { - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(@"mockID"); - id mockSignInResult = OCMClassMock([GIDSignInResult class]); - OCMStub([mockSignInResult user]).andReturn(mockUser); + TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; + fakeUser.userID = @"mockID"; + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeUser; - self.fakeSignIn.signInResult = mockSignInResult; + self.fakeSignIn.signInResult = fakeSignInResult; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeScopesAlreadyGranted @@ -472,26 +584,20 @@ - (void)testSignInExceptionReturnsError { #pragma mark - refreshedAuthorizationTokens - (void)testRefreshTokens { - id mockUser = [self signedInMockUser]; - NSString *userIdentifier = ((GIDGoogleUser *)mockUser).userID; - id mockUserResponse = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUserResponse userID]).andReturn(userIdentifier); - - id mockIdToken = OCMClassMock([GIDToken class]); - OCMStub([mockIdToken tokenString]).andReturn(@"mockIdToken"); - OCMStub([mockUserResponse idToken]).andReturn(mockIdToken); - - id mockAccessToken = OCMClassMock([GIDToken class]); - OCMStub([mockAccessToken tokenString]).andReturn(@"mockAccessToken"); - OCMStub([mockUserResponse accessToken]).andReturn(mockAccessToken); + TestGoogleUser *fakeUser = [self addSignedInUser]; + // TestGoogleUser passes itself as the result's user property, so set the + // fake result data on this object. + TestToken *fakeIDToken = [[TestToken alloc] init]; + fakeIDToken.tokenString = @"mockIdToken"; + fakeUser.idToken = fakeIDToken; - [[mockUser stub] - refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:mockUserResponse, - [NSNull null], nil]]; + TestToken *fakeAccessToken = [[TestToken alloc] init]; + fakeAccessToken.tokenString = @"mockAccessToken"; + fakeUser.accessToken = fakeAccessToken; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin - refreshedAuthorizationTokensForUser:userIdentifier + refreshedAuthorizationTokensForUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(error); XCTAssertNil(result.error); @@ -520,16 +626,15 @@ - (void)testRefreshTokensUnkownUser { } - (void)testRefreshTokensNoAuthKeychainError { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; - [[mockUser stub] refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], - sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - [self.plugin refreshedAuthorizationTokensForUser:((GIDGoogleUser *)mockUser).userID + [self.plugin refreshedAuthorizationTokensForUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(error); XCTAssertNil(result.success); @@ -541,16 +646,15 @@ - (void)testRefreshTokensNoAuthKeychainError { } - (void)testRefreshTokensCancelledError { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeCanceled userInfo:nil]; - [[mockUser stub] refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], - sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - [self.plugin refreshedAuthorizationTokensForUser:((GIDGoogleUser *)mockUser).userID + [self.plugin refreshedAuthorizationTokensForUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(error); XCTAssertNil(result.success); @@ -562,16 +666,15 @@ - (void)testRefreshTokensCancelledError { } - (void)testRefreshTokensURLError { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:nil]; - [[mockUser stub] refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], - sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - [self.plugin refreshedAuthorizationTokensForUser:((GIDGoogleUser *)mockUser).userID + [self.plugin refreshedAuthorizationTokensForUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(result.error); XCTAssertNil(result.success); @@ -585,14 +688,13 @@ - (void)testRefreshTokensURLError { } - (void)testRefreshTokensUnknownError { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; - [[mockUser stub] refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], - sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - [self.plugin refreshedAuthorizationTokensForUser:((GIDGoogleUser *)mockUser).userID + [self.plugin refreshedAuthorizationTokensForUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(result.success); XCTAssertEqualObjects(error.code, @"BogusDomain: 42"); @@ -603,6 +705,29 @@ - (void)testRefreshTokensUnknownError { #pragma mark - addScopes +- (void)testRequestScopesPassesScopes { + TestGoogleUser *fakeUser = [self addSignedInUser]; + // Create a different instance to return in the result, to avoid a retain cycle. + TestGoogleUser *fakeResultUser = [[TestGoogleUser alloc] init]; + fakeResultUser.userID = fakeUser.userID; + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeResultUser; + fakeUser.result = fakeSignInResult; + + NSArray *scopes = @[ @"mockScope1" ]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; + [self.plugin addScopes:@[ @"mockScope1" ] + forUser:fakeUser.userID + completion:^(FSISignInResult *result, FlutterError *error) { + XCTAssertNil(error); + XCTAssertNotNil(result.success); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + XCTAssertEqual(fakeUser.requestedScopes.firstObject, scopes.firstObject); +} + - (void)testRequestScopesResultErrorIfNotSignedIn { XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin addScopes:@[ @"mockScope1" ] @@ -617,18 +742,16 @@ - (void)testRequestScopesResultErrorIfNotSignedIn { } - (void)testRequestScopesIfNoMissingScope { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeScopesAlreadyGranted userInfo:nil]; - [self configureMock:[mockUser stub] - forAddScopes:@[ @"mockScope1" ] - completion:[OCMArg invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin addScopes:@[ @"mockScope1" ] - forUser:((GIDGoogleUser *)mockUser).userID + forUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(error); XCTAssertNil(result.success); @@ -639,18 +762,16 @@ - (void)testRequestScopesIfNoMissingScope { } - (void)testRequestScopesResultErrorIfMismatchingUser { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeMismatchWithCurrentUser userInfo:nil]; - [self configureMock:[mockUser stub] - forAddScopes:@[ @"mockScope1" ] - completion:[OCMArg invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin addScopes:@[ @"mockScope1" ] - forUser:((GIDGoogleUser *)mockUser).userID + forUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(error); XCTAssertNil(result.success); @@ -661,16 +782,14 @@ - (void)testRequestScopesResultErrorIfMismatchingUser { } - (void)testRequestScopesWithUnknownError { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; NSError *sdkError = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; - [self configureMock:[mockUser stub] - forAddScopes:@[ @"mockScope1" ] - completion:[OCMArg invokeBlockWithArgs:[NSNull null], sdkError, nil]]; + fakeUser.error = sdkError; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin addScopes:@[ @"mockScope1" ] - forUser:((GIDGoogleUser *)mockUser).userID + forUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(result); XCTAssertEqualObjects(error.code, @"BogusDomain: 42"); @@ -680,40 +799,33 @@ - (void)testRequestScopesWithUnknownError { } - (void)testRequestScopesException { - id mockUser = [self signedInMockUser]; + TestGoogleUser *fakeUser = [self addSignedInUser]; - OCMExpect([self configureMock:mockUser forAddScopes:@[] completion:OCMOCK_ANY]) - .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); + fakeUser.exception = [NSException exceptionWithName:@"MockName" + reason:@"MockReason" + userInfo:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin addScopes:@[] - forUser:((GIDGoogleUser *)mockUser).userID + forUser:fakeUser.userID completion:^(FSISignInResult *result, FlutterError *error) { XCTAssertNil(result); XCTAssertEqualObjects(error.code, @"request_scopes"); XCTAssertEqualObjects(error.message, @"MockReason"); XCTAssertEqualObjects(error.details, @"MockName"); + [expectation fulfill]; }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } #pragma mark - Utils -- (id)signedInMockUser { - NSString *identifier = @"mockID"; - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(identifier); - self.plugin.usersByIdentifier[identifier] = mockUser; - return mockUser; -} - -- (void)configureMock:(id)mock - forAddScopes:(NSArray *)scopes - completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult, - NSError *_Nullable error))completion { -#if TARGET_OS_OSX - [mock addScopes:scopes presentingWindow:OCMOCK_ANY completion:completion]; -#else - [mock addScopes:scopes presentingViewController:OCMOCK_ANY completion:completion]; -#endif +- (TestGoogleUser *)addSignedInUser { + NSString *identifier = @"fakeID"; + TestGoogleUser *user = [[TestGoogleUser alloc] init]; + user.userID = identifier; + self.plugin.usersByIdentifier[identifier] = user; + return user; } @end diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h index 4896c5af603..bebee5f9dfd 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIGoogleSignInProtocols.h @@ -73,16 +73,15 @@ NS_ASSUME_NONNULL_BEGIN - (void)addScopes:(NSArray *)scopes presentingViewController:(UIViewController *)presentingViewController - completion: - (nullable void (^)(NSObject *_Nullable signInResult, - NSError *_Nullable error))completion + completion:(nullable void (^)(NSObject *_Nullable result, + NSError *_Nullable error))completion NS_EXTENSION_UNAVAILABLE("The add scopes flow is not supported in App Extensions."); #elif TARGET_OS_OSX - (void)addScopes:(NSArray *)scopes presentingWindow:(NSWindow *)presentingWindow - completion:(nullable void (^)(NSObject *_Nullable signInResult, + completion:(nullable void (^)(NSObject *_Nullable result, NSError *_Nullable error))completion; #endif From c9f3b0974ab0bad1208abdedfe5a48bd83c92210 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 16:39:24 -0400 Subject: [PATCH 08/16] Remove some unnecessary mock setup --- .../google_sign_in_ios/darwin/Tests/GoogleSignInTests.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 0826ca7e0da..39f4ee676a3 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -234,7 +234,6 @@ - (void)addScopes:(NSArray *)scopes @interface FLTGoogleSignInPluginTest : XCTestCase -@property(strong, nonatomic) NSObject *mockBinaryMessenger; @property(strong, nonatomic) NSObject *mockPluginRegistrar; @property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; @property(strong, nonatomic) TestSignIn *fakeSignIn; @@ -246,15 +245,12 @@ @implementation FLTGoogleSignInPluginTest - (void)setUp { [super setUp]; - self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); self.mockPluginRegistrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); self.fakeSignIn = [[TestSignIn alloc] init]; - OCMStub(self.mockPluginRegistrar.messenger).andReturn(self.mockBinaryMessenger); self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn registrar:self.mockPluginRegistrar]; - [FLTGoogleSignInPlugin registerWithRegistrar:self.mockPluginRegistrar]; NSString *plistPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"GoogleService-Info" From 1acc9013f40fc3a090f65245d020c0d14c520de7 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 16:59:58 -0400 Subject: [PATCH 09/16] Add view provider protocol, remove OCMock --- .../darwin/Tests/GoogleSignInTests.m | 31 +++++--- .../FLTGoogleSignInPlugin.m | 68 +++++++++++----- .../FLTGoogleSignInPlugin_Test.h | 9 ++- .../google_sign_in_ios/FSIViewProvider.h | 27 +++++++ .../ios/Runner.xcodeproj/project.pbxproj | 21 +---- .../xcshareddata/swiftpm/Package.resolved | 77 +++++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 69 +++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 77 +++++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 76 ++++++++++++++++++ 9 files changed, 403 insertions(+), 52 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIViewProvider.h create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 39f4ee676a3..88c9895f8c6 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -16,8 +16,19 @@ #endif @import GoogleSignIn; -// OCMock library doesn't generate a valid modulemap. -#import +/// Test implementation of @c FSIViewProvider. +@interface TestViewProvider : NSObject +#if TARGET_OS_OSX +/// The view containing the Flutter content. +@property(nonatomic, nullable) NSView *view +#else +/// The view controller containing the Flutter content. +@property(nonatomic, nullable) UIViewController *viewController; +#endif + @end + +@implementation TestViewProvider +@end /// Test implementation of @c FSIGIDSignIn. @interface TestSignIn : NSObject @@ -234,7 +245,7 @@ - (void)addScopes:(NSArray *)scopes @interface FLTGoogleSignInPluginTest : XCTestCase -@property(strong, nonatomic) NSObject *mockPluginRegistrar; +@property(strong, nonatomic) TestViewProvider *viewProvider; @property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; @property(strong, nonatomic) TestSignIn *fakeSignIn; @property(strong, nonatomic) NSDictionary *googleServiceInfo; @@ -245,12 +256,12 @@ @implementation FLTGoogleSignInPluginTest - (void)setUp { [super setUp]; - self.mockPluginRegistrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + self.viewProvider = [[TestViewProvider alloc] init]; self.fakeSignIn = [[TestSignIn alloc] init]; self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar]; + viewProvider:self.viewProvider]; NSString *plistPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"GoogleService-Info" @@ -281,7 +292,7 @@ - (void)testDisconnect { - (void)testInitNoClientIdNoError { // Init plugin without GoogleService-Info.plist. self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar + viewProvider:self.viewProvider googleServiceProperties:nil]; // init call does not provide a clientId. @@ -296,7 +307,7 @@ - (void)testInitNoClientIdNoError { - (void)testInitGoogleServiceInfoPlist { self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar + viewProvider:self.viewProvider googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = [FSIPlatformConfigurationParams makeWithClientId:nil @@ -317,7 +328,7 @@ - (void)testInitGoogleServiceInfoPlist { - (void)testInitDynamicClientIdNullDomain { // Init plugin without GoogleService-Info.plist. self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar + viewProvider:self.viewProvider googleServiceProperties:nil]; FSIPlatformConfigurationParams *params = @@ -335,7 +346,7 @@ - (void)testInitDynamicClientIdNullDomain { - (void)testInitDynamicServerClientIdNullDomain { self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar + viewProvider:self.viewProvider googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = [FSIPlatformConfigurationParams makeWithClientId:nil @@ -412,7 +423,7 @@ - (void)testRestorePreviousSignInWithError { - (void)testSignIn { self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn - registrar:self.mockPluginRegistrar + viewProvider:self.viewProvider googleServiceProperties:self.googleServiceInfo]; TestGoogleUser *fakeUser = [[TestGoogleUser alloc] init]; fakeUser.userID = @"mockID"; diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index 4f51e637c50..d6cab055e8f 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -8,6 +8,7 @@ #import #import "FSIGoogleSignInProtocols.h" +#import "FSIViewProvider.h" // The key within `GoogleService-Info.plist` used to hold the application's // client id. See https://developers.google.com/identity/sign-in/ios/start @@ -98,6 +99,17 @@ static FSIGoogleSignInErrorCode FSIPigeonErrorCodeForGIDSignInErrorCode(NSIntege // TODO(stuarmorgan): Replace these with protocol extensions when migrating to Swift. #pragma mark - Passthrough Wrappers +/// Implementation of @c FSIViewProvider that passes through to the registrar. +@interface FSIDefaultViewProvider : NSObject +/// Returns a provider backed by the given registrar. +- (instancetype)initWithRegistrar:(NSObject *)registrar + NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +/// The registrar backing the provider. +@property(strong, readonly) NSObject *registrar; +@end + /// Implementation of @c FSIGIDSignIn that passes through to GIDSignIn. @interface FSIGIDSignInWrapper : NSObject @@ -152,6 +164,31 @@ - (instancetype)initWithToken:(nullable GIDToken *)token; #pragma mark - +@implementation FSIDefaultViewProvider +- (instancetype)initWithRegistrar:(NSObject *)registrar { + self = [super init]; + if (self) { + _registrar = registrar; + } + return self; +} + +#if TARGET_OS_OSX +- (NSView *)view { + return self.registrar.view; +} +#else +- (UIViewController *)viewController { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 + return UIApplication.sharedApplication.keyWindow.rootViewController; +#pragma clang diagnostic pop +} +#endif +@end + @implementation FSIGIDSignInWrapper - (instancetype)init { @@ -406,37 +443,38 @@ @interface FLTGoogleSignInPlugin () // The contents of GoogleService-Info.plist, if it exists. @property(nonatomic, nullable) NSDictionary *googleServiceProperties; -// The plugin registrar, for querying views. -@property(nonatomic, nonnull) id registrar; +// The view provider, to access the current Flutter view. +@property(nonatomic, nonnull) NSObject *viewProvider; @end @implementation FLTGoogleSignInPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] initWithRegistrar:registrar]; + FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] + initWithViewProvider:[[FSIDefaultViewProvider alloc] initWithRegistrar:registrar]]; [registrar addApplicationDelegate:instance]; SetUpFSIGoogleSignInApi(registrar.messenger, instance); } -- (instancetype)initWithRegistrar:(NSObject *)registrar { - return [self initWithSignIn:[[FSIGIDSignInWrapper alloc] init] registrar:registrar]; +- (instancetype)initWithViewProvider:(NSObject *)viewProvider { + return [self initWithSignIn:[[FSIGIDSignInWrapper alloc] init] viewProvider:viewProvider]; } - (instancetype)initWithSignIn:(NSObject *)signIn - registrar:(NSObject *)registrar { + viewProvider:(NSObject *)viewProvider { return [self initWithSignIn:signIn - registrar:registrar + viewProvider:viewProvider googleServiceProperties:FSILoadGoogleServiceInfo()]; } - (instancetype)initWithSignIn:(NSObject *)signIn - registrar:(NSObject *)registrar + viewProvider:(NSObject *)viewProvider googleServiceProperties:(nullable NSDictionary *)googleServiceProperties { self = [super init]; if (self) { _signIn = signIn; - _registrar = registrar; + _viewProvider = viewProvider; _googleServiceProperties = googleServiceProperties; _usersByIdentifier = [[NSMutableDictionary alloc] init]; @@ -587,7 +625,7 @@ - (void)signInWithHint:(nullable NSString *)hint completion:(void (^)(NSObject *_Nullable signInResult, NSError *_Nullable error))completion { #if TARGET_OS_OSX - [self.signIn signInWithPresentingWindow:self.registrar.view.window + [self.signIn signInWithPresentingWindow:self.viewProvider.view.window hint:hint additionalScopes:additionalScopes nonce:nonce @@ -607,7 +645,7 @@ - (void)addScopes:(nonnull NSArray *)scopes completion:(void (^)(NSObject *_Nullable signInResult, NSError *_Nullable error))completion { #if TARGET_OS_OSX - [user addScopes:scopes presentingWindow:self.registrar.view.window completion:completion]; + [user addScopes:scopes presentingWindow:self.viewProvider.view.window completion:completion]; #else [user addScopes:scopes presentingViewController:[self topViewController] completion:completion]; #endif @@ -686,13 +724,7 @@ - (void)didSignInForUser:(nonnull NSObject *)user #if TARGET_OS_IOS - (UIViewController *)topViewController { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // TODO(stuartmorgan) Provide a non-deprecated codepath. See - // https://github.com/flutter/flutter/issues/104117 - return [self topViewControllerFromViewController:[UIApplication sharedApplication] - .keyWindow.rootViewController]; -#pragma clang diagnostic pop + return [self topViewControllerFromViewController:self.viewProvider.viewController]; } /// This method recursively iterate through the view hierarchy diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h index 586ca1fbdf3..7e0f6017dfa 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FLTGoogleSignInPlugin_Test.h @@ -9,6 +9,7 @@ #import #import "FSIGoogleSignInProtocols.h" +#import "FSIViewProvider.h" NS_ASSUME_NONNULL_BEGIN @@ -23,16 +24,16 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic) NSMutableDictionary *> *usersByIdentifier; -/// Inject @c FlutterPluginRegistrar for testing. -- (instancetype)initWithRegistrar:(NSObject *)registrar; +/// Inject view provider for testing. +- (instancetype)initWithViewProvider:(NSObject *)viewProvider; /// Inject @c NSObject for testing. - (instancetype)initWithSignIn:(NSObject *)signIn - registrar:(NSObject *)registrar; + viewProvider:(NSObject *)viewProvider; /// Inject @c NSObject and @c googleServiceProperties for testing. - (instancetype)initWithSignIn:(NSObject *)signIn - registrar:(NSObject *)registrar + viewProvider:(NSObject *)viewProvider googleServiceProperties:(nullable NSDictionary *)googleServiceProperties NS_DESIGNATED_INITIALIZER; diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIViewProvider.h b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIViewProvider.h new file mode 100644 index 00000000000..5c1a48ae519 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/include/google_sign_in_ios/FSIViewProvider.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if TARGET_OS_OSX +@import Cocoa; +@import FlutterMacOS; +#else +@import Flutter; +@import UIKit; +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// Protocol for obtaining the view containing the Flutter content. +@protocol FSIViewProvider +@required +#if TARGET_OS_OSX +/// The view containing the Flutter content. +@property(nonatomic, readonly, nullable) NSView *view; +#else +/// The view controller containing the Flutter content. +@property(nonatomic, readonly, nullable) UIViewController *viewController; +#endif +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj index f00cbf67f94..6c78727f6d7 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,11 +3,10 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ - 40A6CE2A2D499A7800DE3915 /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 40A6CE292D499A7800DE3915 /* OCMock */; }; 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 78A36DA12AF5761E00CBFD43 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; @@ -97,7 +96,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 40A6CE2A2D499A7800DE3915 /* OCMock in Frameworks */, C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -308,7 +306,6 @@ mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, - 40A6CE282D499A7800DE3915 /* XCRemoteSwiftPackageReference "ocmock" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; @@ -756,23 +753,7 @@ }; /* End XCLocalSwiftPackageReference section */ -/* Begin XCRemoteSwiftPackageReference section */ - 40A6CE282D499A7800DE3915 /* XCRemoteSwiftPackageReference "ocmock" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/erikdoe/ocmock"; - requirement = { - kind = revision; - revision = fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - 40A6CE292D499A7800DE3915 /* OCMock */ = { - isa = XCSwiftPackageProductDependency; - package = 40A6CE282D499A7800DE3915 /* XCRemoteSwiftPackageReference "ocmock" */; - productName = OCMock; - }; 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { isa = XCSwiftPackageProductDependency; productName = FlutterGeneratedPluginSwiftPackage; diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000000..bc6dc63f212 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,77 @@ +{ + "originHash" : "4b36b64d9a071f871b5e5ecf435f3e097843030f79026880b7d9ef1f081d563d", + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", + "version" : "2.0.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", + "version" : "9.0.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "ocmock", + "kind" : "remoteSourceControl", + "location" : "https://github.com/erikdoe/ocmock", + "state" : { + "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 3 +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000000..241e1e9e7a8 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,69 @@ +{ + "originHash" : "8c840eeeda2581c1fabf915461049b3c7ff6cd63f7dabd8d66b0814611e761da", + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", + "version" : "2.0.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", + "version" : "9.0.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 3 +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000000..adf77cd1fe4 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,77 @@ +{ + "originHash" : "55de946155a69d2224772ed1784cce32f8362a10a2279d38a25e5d810d56634f", + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", + "version" : "2.0.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", + "version" : "9.0.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "ocmock", + "kind" : "remoteSourceControl", + "location" : "https://github.com/erikdoe/ocmock", + "state" : { + "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 3 +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000000..34334fbe7d8 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,76 @@ +{ + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", + "version" : "2.0.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", + "version" : "9.0.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "ocmock", + "kind" : "remoteSourceControl", + "location" : "https://github.com/erikdoe/ocmock", + "state" : { + "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 2 +} From 615930427e71ad65f62efd56256108c27ca1d533 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 19:24:49 -0400 Subject: [PATCH 10/16] Add missing configuration passthrough --- .../Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index d6cab055e8f..7b9d7d4c278 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -199,6 +199,14 @@ - (instancetype)init { return self; } +- (GIDConfiguration *)configuration { + return self.signIn.configuration; +} + +- (void)setConfiguration:(GIDConfiguration *)configuration { + self.signIn.configuration = configuration; +} + - (BOOL)handleURL:(NSURL *)url { return [self.signIn handleURL:url]; } From 29f617801d215f21b8b8cba09e91a90ac8cf62b5 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 19:29:37 -0400 Subject: [PATCH 11/16] macOS test compilation --- .../google_sign_in_ios/darwin/Tests/GoogleSignInTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 88c9895f8c6..ffd4765f737 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -20,7 +20,7 @@ @interface TestViewProvider : NSObject #if TARGET_OS_OSX /// The view containing the Flutter content. -@property(nonatomic, nullable) NSView *view +@property(nonatomic, nullable) NSView *view; #else /// The view controller containing the Flutter content. @property(nonatomic, nullable) UIViewController *viewController; @@ -227,7 +227,7 @@ - (void)addScopes:(NSArray *)scopes presentingWindow:(NSWindow *)presentingWindow completion:(nullable void (^)(NSObject *_Nullable result, NSError *_Nullable error))completion { - self.scopes = scopes; + self.requestedScopes = scopes; self.presentingWindow = presentingWindow; if (self.exception) { @throw self.exception; From 10f9367b1894b622f6113e1e11447ff946638799 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 23 Oct 2025 19:31:39 -0400 Subject: [PATCH 12/16] Remove OCMock from macOS project --- .../macos/Runner.xcodeproj/project.pbxproj | 26 +++---------------- .../xcshareddata/swiftpm/Package.resolved | 11 ++------ 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj index 29c38db0956..99e3f601ab4 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXAggregateTarget section */ @@ -28,7 +28,6 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 403C71332C66A4F70034A230 /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 403C71322C66A4F70034A230 /* OCMock */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; D7B8415A3D20001DE212873D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A19720E5189178660264EC3F /* Pods_Runner.framework */; }; F107B1B7E4ECB85727EC4286 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BD1DCE969A7E7C76D9CF93C /* Pods_RunnerTests.framework */; }; @@ -99,7 +98,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 403C71332C66A4F70034A230 /* OCMock in Frameworks */, F107B1B7E4ECB85727EC4286 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -234,7 +232,6 @@ ); name = RunnerTests; packageProductDependencies = ( - 403C71322C66A4F70034A230 /* OCMock */, ); productName = RunnerTests; productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; @@ -304,8 +301,7 @@ ); mainGroup = 33CC10E42044A3C60003C045; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, - 403C71312C66A4F70034A230 /* XCRemoteSwiftPackageReference "ocmock" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; @@ -788,29 +784,13 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; /* End XCLocalSwiftPackageReference section */ -/* Begin XCRemoteSwiftPackageReference section */ - 403C71312C66A4F70034A230 /* XCRemoteSwiftPackageReference "ocmock" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/erikdoe/ocmock"; - requirement = { - kind = revision; - revision = fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - 403C71322C66A4F70034A230 /* OCMock */ = { - isa = XCSwiftPackageProductDependency; - package = 403C71312C66A4F70034A230 /* XCRemoteSwiftPackageReference "ocmock" */; - productName = OCMock; - }; 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { isa = XCSwiftPackageProductDependency; productName = FlutterGeneratedPluginSwiftPackage; diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index 34334fbe7d8..50dda51f47d 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "0f98c30e05c97f0caa8ab4c3349224fd47a71a9a8e72026c1139364b9ed49230", "pins" : [ { "identity" : "app-check", @@ -54,14 +55,6 @@ "version" : "5.0.0" } }, - { - "identity" : "ocmock", - "kind" : "remoteSourceControl", - "location" : "https://github.com/erikdoe/ocmock", - "state" : { - "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" - } - }, { "identity" : "promises", "kind" : "remoteSourceControl", @@ -72,5 +65,5 @@ } } ], - "version" : 2 + "version" : 3 } From 005533e72fa8153ab10db47fff49d16a0eed852f Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 24 Oct 2025 09:55:09 -0400 Subject: [PATCH 13/16] Version bump --- packages/google_sign_in/google_sign_in_ios/CHANGELOG.md | 4 ++++ packages/google_sign_in/google_sign_in_ios/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 4e24b1003aa..66081c5ce81 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.2.3 + +* Internal restructuring to improve testability. + ## 6.2.2 * Updates minimum supported version to iOS 13. diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index 299539a3228..e9bfa9bc0ab 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.2.2 +version: 6.2.3 environment: sdk: ^3.9.0 From c3d03b15d0a04f83c9b80bf4506021b956103032 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 24 Oct 2025 10:05:12 -0400 Subject: [PATCH 14/16] Revert SwiftPM files --- .../xcshareddata/swiftpm/Package.resolved | 77 ------------------- .../xcshareddata/swiftpm/Package.resolved | 69 ----------------- .../xcshareddata/swiftpm/Package.resolved | 77 ------------------- .../xcshareddata/swiftpm/Package.resolved | 69 ----------------- 4 files changed, 292 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index bc6dc63f212..00000000000 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,77 +0,0 @@ -{ - "originHash" : "4b36b64d9a071f871b5e5ecf435f3e097843030f79026880b7d9ef1f081d563d", - "pins" : [ - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", - "version" : "11.2.0" - } - }, - { - "identity" : "appauth-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/openid/AppAuth-iOS.git", - "state" : { - "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", - "version" : "2.0.0" - } - }, - { - "identity" : "googlesignin-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleSignIn-iOS.git", - "state" : { - "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", - "version" : "9.0.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", - "version" : "8.1.0" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", - "version" : "3.5.0" - } - }, - { - "identity" : "gtmappauth", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GTMAppAuth.git", - "state" : { - "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", - "version" : "5.0.0" - } - }, - { - "identity" : "ocmock", - "kind" : "remoteSourceControl", - "location" : "https://github.com/erikdoe/ocmock", - "state" : { - "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - } - ], - "version" : 3 -} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 241e1e9e7a8..00000000000 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,69 +0,0 @@ -{ - "originHash" : "8c840eeeda2581c1fabf915461049b3c7ff6cd63f7dabd8d66b0814611e761da", - "pins" : [ - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", - "version" : "11.2.0" - } - }, - { - "identity" : "appauth-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/openid/AppAuth-iOS.git", - "state" : { - "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", - "version" : "2.0.0" - } - }, - { - "identity" : "googlesignin-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleSignIn-iOS.git", - "state" : { - "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", - "version" : "9.0.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", - "version" : "8.1.0" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", - "version" : "3.5.0" - } - }, - { - "identity" : "gtmappauth", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GTMAppAuth.git", - "state" : { - "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", - "version" : "5.0.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - } - ], - "version" : 3 -} diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index adf77cd1fe4..00000000000 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,77 +0,0 @@ -{ - "originHash" : "55de946155a69d2224772ed1784cce32f8362a10a2279d38a25e5d810d56634f", - "pins" : [ - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", - "version" : "11.2.0" - } - }, - { - "identity" : "appauth-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/openid/AppAuth-iOS.git", - "state" : { - "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", - "version" : "2.0.0" - } - }, - { - "identity" : "googlesignin-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleSignIn-iOS.git", - "state" : { - "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", - "version" : "9.0.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", - "version" : "8.1.0" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", - "version" : "3.5.0" - } - }, - { - "identity" : "gtmappauth", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GTMAppAuth.git", - "state" : { - "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", - "version" : "5.0.0" - } - }, - { - "identity" : "ocmock", - "kind" : "remoteSourceControl", - "location" : "https://github.com/erikdoe/ocmock", - "state" : { - "revision" : "fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - } - ], - "version" : 3 -} diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 50dda51f47d..00000000000 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,69 +0,0 @@ -{ - "originHash" : "0f98c30e05c97f0caa8ab4c3349224fd47a71a9a8e72026c1139364b9ed49230", - "pins" : [ - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", - "version" : "11.2.0" - } - }, - { - "identity" : "appauth-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/openid/AppAuth-iOS.git", - "state" : { - "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", - "version" : "2.0.0" - } - }, - { - "identity" : "googlesignin-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleSignIn-iOS.git", - "state" : { - "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", - "version" : "9.0.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", - "version" : "8.1.0" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", - "version" : "3.5.0" - } - }, - { - "identity" : "gtmappauth", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GTMAppAuth.git", - "state" : { - "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", - "version" : "5.0.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - } - ], - "version" : 3 -} From 5bae39496c5c1891dfcc6e9ce646a1e38b3960ba Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 24 Oct 2025 10:43:04 -0400 Subject: [PATCH 15/16] Property cleaunp --- .../darwin/Tests/GoogleSignInTests.m | 49 ++++++++++--------- .../FLTGoogleSignInPlugin.m | 4 +- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index ffd4765f737..87bf2777c2c 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m @@ -25,7 +25,7 @@ @interface TestViewProvider : NSObject /// The view controller containing the Flutter content. @property(nonatomic, nullable) UIViewController *viewController; #endif - @end +@end @implementation TestViewProvider @end @@ -131,13 +131,14 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow /// Test implementation of @c FSIGIDProfileData. @interface TestProfileData : NSObject -@property(strong, nonatomic) NSString *email; -@property(strong, nonatomic) NSString *name; +@property(nonatomic, readwrite) NSString *email; +@property(nonatomic, readwrite) NSString *name; /// A URL to return from imageURLWithDimension:. -@property(strong, nonatomic) NSURL *imageURL; +@property(nonatomic) NSURL *imageURL; @end -@implementation TestProfileData +@implementation TestProfileData { +} - (BOOL)hasImage { return self.imageURL != nil; @@ -150,8 +151,8 @@ - (NSURL *)imageURLWithDimension:(NSUInteger)dimension { /// Test implementation of @c FSIGIDToken. @interface TestToken : NSObject -@property(strong, nonatomic) NSString *tokenString; -@property(strong, nonatomic) NSDate *expirationDate; +@property(nonatomic, readwrite) NSString *tokenString; +@property(nonatomic, readwrite) NSDate *expirationDate; @end @implementation TestToken @@ -159,8 +160,8 @@ @implementation TestToken /// Test implementation of @c FSIGIDSignInResult. @interface TestSignInResult : NSObject -@property(strong, nonatomic) NSObject *user; -@property(strong, nonatomic, nullable) NSString *serverAuthCode; +@property(nonatomic, readwrite) NSObject *user; +@property(nonatomic, readwrite, nullable) NSString *serverAuthCode; @end @implementation TestSignInResult @@ -168,28 +169,28 @@ @implementation TestSignInResult /// Test implementation of @c FSIGIDGoogleUser. @interface TestGoogleUser : NSObject -@property(strong, nonatomic, nullable) NSString *userID; -@property(strong, nonatomic, nullable) NSObject *profile; -@property(strong, nonatomic, nullable) NSArray *grantedScopes; -@property(strong, nonatomic) NSObject *accessToken; -@property(strong, nonatomic) NSObject *refreshToken; -@property(strong, nonatomic, nullable) NSObject *idToken; +@property(nonatomic, readwrite, nullable) NSString *userID; +@property(nonatomic, readwrite, nullable) NSObject *profile; +@property(nonatomic, readwrite, nullable) NSArray *grantedScopes; +@property(nonatomic, readwrite) NSObject *accessToken; +@property(nonatomic, readwrite) NSObject *refreshToken; +@property(nonatomic, readwrite, nullable) NSObject *idToken; /// An exception to throw from methods. @property(nonatomic, nullable) NSException *exception; /// The result to return from addScopes:presentingViewController:completion:. -@property(strong, nonatomic, nullable) NSObject *result; +@property(nonatomic, nullable) NSObject *result; /// The error to return from methods. -@property(strong, nonatomic, nullable) NSError *error; +@property(nonatomic, nullable) NSError *error; // Values passed as parameters. -@property(strong, nonatomic, nullable) NSArray *requestedScopes; +@property(nonatomic, copy, nullable) NSArray *requestedScopes; #if TARGET_OS_IOS || TARGET_OS_MACCATALYST -@property(strong, nonatomic, nullable) UIViewController *presentingViewController; +@property(nonatomic, nullable) UIViewController *presentingViewController; #else -@property(strong, nonatomic, nullable) NSWindow *presentingWindow; +@property(nonatomic, nullable) NSWindow *presentingWindow; #endif @end @@ -245,10 +246,10 @@ - (void)addScopes:(NSArray *)scopes @interface FLTGoogleSignInPluginTest : XCTestCase -@property(strong, nonatomic) TestViewProvider *viewProvider; -@property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; -@property(strong, nonatomic) TestSignIn *fakeSignIn; -@property(strong, nonatomic) NSDictionary *googleServiceInfo; +@property(nonatomic) TestViewProvider *viewProvider; +@property(nonatomic) FLTGoogleSignInPlugin *plugin; +@property(nonatomic) TestSignIn *fakeSignIn; +@property(nonatomic, copy) NSDictionary *googleServiceInfo; @end diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index 7b9d7d4c278..715d87df3ce 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -107,14 +107,14 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar - (instancetype)init NS_UNAVAILABLE; /// The registrar backing the provider. -@property(strong, readonly) NSObject *registrar; +@property(readonly) NSObject *registrar; @end /// Implementation of @c FSIGIDSignIn that passes through to GIDSignIn. @interface FSIGIDSignInWrapper : NSObject /// The underlying GIDSignIn instance. -@property(strong, readonly) GIDSignIn *signIn; +@property(readonly) GIDSignIn *signIn; @end From 265c7eff67f6006d872ec76c7d1f16605a4b2579 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 24 Oct 2025 10:47:10 -0400 Subject: [PATCH 16/16] Designated initializers --- .../google_sign_in_ios/FLTGoogleSignInPlugin.m | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m index 715d87df3ce..88812194f3d 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios/Sources/google_sign_in_ios/FLTGoogleSignInPlugin.m @@ -122,7 +122,8 @@ @interface FSIGIDSignInWrapper : NSObject /// implementation in unit tests. @interface FSIGIDSignInResultWrapper : NSObject -- (instancetype)initWithResult:(nullable GIDSignInResult *)result; +- (instancetype)initWithResult:(nullable GIDSignInResult *)result NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; /// The underlying GIDSignInResult instance. @property(nonatomic, nullable) GIDSignInResult *result; @@ -133,7 +134,8 @@ - (instancetype)initWithResult:(nullable GIDSignInResult *)result; /// implementation in unit tests. @interface FSIGIDGoogleUserWrapper : NSObject -- (instancetype)initWithUser:(nullable GIDGoogleUser *)user; +- (instancetype)initWithUser:(nullable GIDGoogleUser *)user NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; /// The underlying GIDGoogleUser instance. @property(nonatomic, nullable) GIDGoogleUser *user; @@ -144,7 +146,9 @@ - (instancetype)initWithUser:(nullable GIDGoogleUser *)user; /// implementation in unit tests. @interface FSIGIDProfileDataWrapper : NSObject -- (instancetype)initWithProfileData:(nullable GIDProfileData *)profileData; +- (instancetype)initWithProfileData:(nullable GIDProfileData *)profileData + NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; /// The underlying GIDProfileData instance. @property(nonatomic, nullable) GIDProfileData *profileData; @@ -155,7 +159,8 @@ - (instancetype)initWithProfileData:(nullable GIDProfileData *)profileData; /// implementation in unit tests. @interface FSIGIDTokenWrapper : NSObject -- (instancetype)initWithToken:(nullable GIDToken *)token; +- (instancetype)initWithToken:(nullable GIDToken *)token NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; /// The underlying GIDToken instance. @property(nonatomic, nullable) GIDToken *token;