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/darwin/Tests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/darwin/Tests/GoogleSignInTests.m index 8daa00077f8..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 @@ -16,16 +16,240 @@ #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 + +// To cause methods to throw an exception. +@property(nonatomic, nullable) NSException *exception; + +// Results to use in completion callbacks. +@property(nonatomic, nullable) NSObject *user; +@property(nonatomic, nullable) NSError *error; +@property(nonatomic, nullable) NSObject *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 + +/// Whether @c signOut was called. +@property(nonatomic) BOOL signOutCalled; + +@end + +@implementation TestSignIn +@synthesize configuration; + +- (BOOL)handleURL:(NSURL *)url { + return YES; +} + +- (void)restorePreviousSignInWithCompletion: + (nullable void (^)(NSObject *_Nullable user, + NSError *_Nullable error))completion { + if (self.exception) { + @throw self.exception; + } + if (completion) { + 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 + nonce:(nullable NSString *)nonce + completion:(nullable void (^)( + NSObject *_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.signInResult ? nil : self.error); + } +} +#else +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + completion: + (nullable void (^)(NSObject *_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.signInResult ? nil : self.error); + } +} +#endif + +@end + +/// Test implementation of @c FSIGIDProfileData. +@interface TestProfileData : NSObject +@property(nonatomic, readwrite) NSString *email; +@property(nonatomic, readwrite) NSString *name; +/// A URL to return from imageURLWithDimension:. +@property(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(nonatomic, readwrite) NSString *tokenString; +@property(nonatomic, readwrite) NSDate *expirationDate; +@end + +@implementation TestToken +@end + +/// Test implementation of @c FSIGIDSignInResult. +@interface TestSignInResult : NSObject +@property(nonatomic, readwrite) NSObject *user; +@property(nonatomic, readwrite, nullable) NSString *serverAuthCode; +@end + +@implementation TestSignInResult +@end + +/// Test implementation of @c FSIGIDGoogleUser. +@interface TestGoogleUser : NSObject +@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(nonatomic, nullable) NSObject *result; + +/// The error to return from methods. +@property(nonatomic, nullable) NSError *error; + +// Values passed as parameters. +@property(nonatomic, copy, nullable) NSArray *requestedScopes; +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +@property(nonatomic, nullable) UIViewController *presentingViewController; +#else +@property(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.requestedScopes = 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; -@property(strong, nonatomic) NSObject *mockPluginRegistrar; -@property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; -@property(strong, nonatomic) id mockSignIn; -@property(strong, nonatomic) NSDictionary *googleServiceInfo; +@property(nonatomic) TestViewProvider *viewProvider; +@property(nonatomic) FLTGoogleSignInPlugin *plugin; +@property(nonatomic) TestSignIn *fakeSignIn; +@property(nonatomic, copy) NSDictionary *googleServiceInfo; @end @@ -33,16 +257,12 @@ @implementation FLTGoogleSignInPluginTest - (void)setUp { [super setUp]; - self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); - self.mockPluginRegistrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + self.viewProvider = [[TestViewProvider alloc] init]; - 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 - registrar:self.mockPluginRegistrar]; - [FLTGoogleSignInPlugin registerWithRegistrar:self.mockPluginRegistrar]; + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider]; NSString *plistPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"GoogleService-Info" @@ -55,14 +275,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); @@ -75,8 +292,8 @@ - (void)testDisconnect { - (void)testInitNoClientIdNoError { // Init plugin without GoogleService-Info.plist. - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn - registrar:self.mockPluginRegistrar + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider googleServiceProperties:nil]; // init call does not provide a clientId. @@ -90,79 +307,63 @@ - (void)testInitNoClientIdNoError { } - (void)testInitGoogleServiceInfoPlist { - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn - registrar:self.mockPluginRegistrar + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = [FSIPlatformConfigurationParams makeWithClientId:nil 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 - registrar:self.mockPluginRegistrar + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider 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 - registrar:self.mockPluginRegistrar + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider googleServiceProperties:self.googleServiceInfo]; FSIPlatformConfigurationParams *params = [FSIPlatformConfigurationParams makeWithClientId:nil 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 { @@ -171,32 +372,20 @@ - (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 - (void)testSignInSilently { - id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([mockUser userID]).andReturn(@"mockID"); - - [[self.mockSignIn stub] - restorePreviousSignInWithCompletion:[OCMArg - invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + 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) { @@ -219,10 +408,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) { @@ -237,34 +423,28 @@ - (void)testRestorePreviousSignInWithError { #pragma mark - signIn - (void)testSignIn { - self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn - registrar:self.mockPluginRegistrar + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.fakeSignIn + viewProvider:self.viewProvider 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); - - id mockSignInResult = OCMClassMock([GIDSignInResult class]); - 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]]; + fakeUser.profile = fakeUserProfile; + TestToken *fakeAccessToken = [[TestToken alloc] init]; + fakeAccessToken.tokenString = accessToken; + fakeUser.accessToken = fakeAccessToken; + + TestSignInResult *fakeSignInResult = [[TestSignInResult alloc] init]; + fakeSignInResult.user = fakeUser; + fakeSignInResult.serverAuthCode = serverAuthCode; + + self.fakeSignIn.signInResult = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -281,8 +461,6 @@ - (void)testSignIn { [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - - OCMVerifyAll(self.mockSignIn); } - (void)testSignInWithScopeHint { @@ -293,19 +471,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 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 = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:requestedScopes @@ -318,7 +490,8 @@ - (void)testSignInWithScopeHint { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerifyAll(self.mockSignIn); + XCTAssertTrue([[NSSet setWithArray:self.fakeSignIn.additionalScopes] + isEqualToSet:[NSSet setWithArray:requestedScopes]]); } - (void)testSignInWithNonce { @@ -329,17 +502,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 configureMock:[self.mockSignIn expect] - forSignInWithHint:nil - additionalScopes:OCMOCK_ANY - nonce:nonce - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = fakeSignInResult; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithScopeHint:@[] @@ -352,27 +521,21 @@ - (void)testSignInWithNonce { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerifyAll(self.mockSignIn); + XCTAssertEqualObjects(self.fakeSignIn.nonce, nonce); } - (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 configureMock:[self.mockSignIn stub] - forSignInWithHint:nil - additionalScopes:OCMOCK_ANY - nonce:nil - completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; + self.fakeSignIn.signInResult = fakeSignInResult; 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:@[] @@ -390,11 +553,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:@[] @@ -411,12 +570,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:@[] @@ -436,26 +592,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); @@ -484,16 +634,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); @@ -505,16 +654,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); @@ -526,16 +674,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); @@ -549,14 +696,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"); @@ -567,6 +713,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" ] @@ -581,18 +750,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); @@ -603,18 +770,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); @@ -625,16 +790,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"); @@ -644,60 +807,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 -} -- (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 +- (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/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..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 @@ -7,6 +7,9 @@ #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 // for more info. @@ -93,42 +96,398 @@ 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(readonly) NSObject *registrar; +@end + +/// Implementation of @c FSIGIDSignIn that passes through to GIDSignIn. +@interface FSIGIDSignInWrapper : NSObject + +/// The underlying GIDSignIn instance. +@property(readonly) GIDSignIn *signIn; + +@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 NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +/// 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 NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +/// 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 + NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +/// 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 NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +/// The underlying GIDToken instance. +@property(nonatomic, nullable) GIDToken *token; + +@end + +#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 { + self = [super init]; + if (self) { + _signIn = GIDSignIn.sharedInstance; + } + 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]; +} + +- (void)restorePreviousSignInWithCompletion: + (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 { + [self.signIn signOut]; +} + +- (void)disconnectWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { + [self.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 (^)( + NSObject *_Nullable signInResult, + NSError *_Nullable error))completion { + [self.signIn signInWithPresentingViewController:presentingViewController + hint:hint + additionalScopes:additionalScopes + nonce:nonce + completion:^(GIDSignInResult *result, NSError *error) { + if (completion) { + completion([[FSIGIDSignInResultWrapper alloc] + initWithResult:result], + error); + } + }]; +} + +#elif TARGET_OS_OSX + +- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow + hint:(nullable NSString *)hint + additionalScopes:(nullable NSArray *)additionalScopes + nonce:(nullable NSString *)nonce + 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.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. @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:GIDSignIn.sharedInstance registrar:registrar]; +- (instancetype)initWithViewProvider:(NSObject *)viewProvider { + return [self initWithSignIn:[[FSIGIDSignInWrapper alloc] init] viewProvider:viewProvider]; } -- (instancetype)initWithSignIn:(GIDSignIn *)signIn - registrar:(NSObject *)registrar { +- (instancetype)initWithSignIn:(NSObject *)signIn + viewProvider:(NSObject *)viewProvider { return [self initWithSignIn:signIn - registrar:registrar + viewProvider:viewProvider googleServiceProperties:FSILoadGoogleServiceInfo()]; } -- (instancetype)initWithSignIn:(GIDSignIn *)signIn - registrar:(NSObject *)registrar +- (instancetype)initWithSignIn:(NSObject *)signIn + viewProvider:(NSObject *)viewProvider googleServiceProperties:(nullable NSDictionary *)googleServiceProperties { self = [super init]; if (self) { _signIn = signIn; - _registrar = registrar; + _viewProvider = viewProvider; _googleServiceProperties = googleServiceProperties; _usersByIdentifier = [[NSMutableDictionary alloc] init]; @@ -173,7 +532,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]; }]; @@ -188,7 +547,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 @@ -202,7 +562,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 @@ -215,7 +575,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 @@ -228,7 +588,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 @@ -244,7 +604,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 @@ -274,10 +635,10 @@ - (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 + [self.signIn signInWithPresentingWindow:self.viewProvider.view.window hint:hint additionalScopes:additionalScopes nonce:nonce @@ -293,11 +654,11 @@ - (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]; + [user addScopes:scopes presentingWindow:self.viewProvider.view.window completion:completion]; #else [user addScopes:scopes presentingViewController:[self topViewController] completion:completion]; #endif @@ -323,7 +684,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, @@ -348,7 +709,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; @@ -376,13 +737,7 @@ - (void)didSignInForUser:(nonnull GIDGoogleUser *)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 6b5b83d8d53..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 @@ -8,30 +8,32 @@ #import -NS_ASSUME_NONNULL_BEGIN +#import "FSIGoogleSignInProtocols.h" +#import "FSIViewProvider.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; +@property(nonatomic) + NSMutableDictionary *> *usersByIdentifier; -/// Inject @c FlutterPluginRegistrar for testing. -- (instancetype)initWithRegistrar:(NSObject *)registrar; +/// Inject view provider for testing. +- (instancetype)initWithViewProvider:(NSObject *)viewProvider; -/// Inject @c GIDSignIn for testing. -- (instancetype)initWithSignIn:(GIDSignIn *)signIn - registrar:(NSObject *)registrar; +/// Inject @c NSObject for testing. +- (instancetype)initWithSignIn:(NSObject *)signIn + viewProvider:(NSObject *)viewProvider; -/// Inject @c GIDSignIn and @c googleServiceProperties for testing. -- (instancetype)initWithSignIn:(GIDSignIn *)signIn - registrar:(NSObject *)registrar +/// Inject @c NSObject and @c googleServiceProperties for testing. +- (instancetype)initWithSignIn:(NSObject *)signIn + 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/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..bebee5f9dfd --- /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,137 @@ +// 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. + +#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 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 result, + 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. +/// +/// See GIDSignIn for documentation, as this should always be implemented as a direct passthrough +/// to GIDSignIn. +@protocol FSIGIDSignIn + +@property(nonatomic, nullable) GIDConfiguration *configuration; + +- (BOOL)handleURL:(NSURL *)url; + +- (void)restorePreviousSignInWithCompletion: + (nullable void (^)(NSObject *_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 (^)( + NSObject *_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 (^)(NSObject *_Nullable signInResult, + NSError *_Nullable error))completion; + +#endif + +@end + +NS_ASSUME_NONNULL_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/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/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/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