Skip to content

Commit 8ec770b

Browse files
[google_sign_in] Convert iOS to Pigeon (#4941)
Replaces the manual method channel code with Pigeon. The Pigeon definition and the Dart code and Dart unit tests are heavily based on the [previous Android migration](#4344). Fixes flutter/flutter#117908
1 parent 7cd2bda commit 8ec770b

File tree

16 files changed

+1592
-629
lines changed

16 files changed

+1592
-629
lines changed

packages/google_sign_in/google_sign_in_ios/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 5.6.4
2+
3+
* Converts platform communication to Pigeon.
4+
15
## 5.6.3
26

37
* Adds pub topics to package metadata.

packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@
273273
97C146E61CF9000F007C117D /* Project object */ = {
274274
isa = PBXProject;
275275
attributes = {
276-
LastUpgradeCheck = 1300;
276+
LastUpgradeCheck = 1430;
277277
ORGANIZATIONNAME = "The Flutter Authors";
278278
TargetAttributes = {
279279
97C146ED1CF9000F007C117D = {

packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1300"
3+
LastUpgradeVersion = "1430"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m

Lines changed: 200 additions & 330 deletions
Large diffs are not rendered by default.

packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44

55
#import <Flutter/Flutter.h>
66

7-
@interface FLTGoogleSignInPlugin : NSObject <FlutterPlugin>
7+
#import "messages.g.h"
8+
9+
@interface FLTGoogleSignInPlugin : NSObject <FlutterPlugin, FSIGoogleSignInApi>
810
@end

packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m

Lines changed: 123 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,9 @@ - (instancetype)init;
7272
@implementation FLTGoogleSignInPlugin
7373

7474
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
75-
FlutterMethodChannel *channel =
76-
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in_ios"
77-
binaryMessenger:[registrar messenger]];
7875
FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] init];
7976
[registrar addApplicationDelegate:instance];
80-
[registrar addMethodCallDelegate:instance channel:channel];
77+
FSIGoogleSignInApiSetup(registrar.messenger, instance);
8178
}
8279

8380
- (instancetype)init {
@@ -105,104 +102,122 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn
105102

106103
#pragma mark - <FlutterPlugin> protocol
107104

108-
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
109-
if ([call.method isEqualToString:@"init"]) {
110-
GIDConfiguration *configuration =
111-
[self configurationWithClientIdArgument:call.arguments[@"clientId"]
112-
serverClientIdArgument:call.arguments[@"serverClientId"]
113-
hostedDomainArgument:call.arguments[@"hostedDomain"]];
114-
if (configuration != nil) {
115-
if ([call.arguments[@"scopes"] isKindOfClass:[NSArray class]]) {
116-
self.requestedScopes = [NSSet setWithArray:call.arguments[@"scopes"]];
117-
}
118-
self.configuration = configuration;
119-
result(nil);
120-
} else {
121-
result([FlutterError errorWithCode:@"missing-config"
105+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
106+
return [self.signIn handleURL:url];
107+
}
108+
109+
#pragma mark - FSIGoogleSignInApi
110+
111+
- (void)initializeSignInWithParameters:(nonnull FSIInitParams *)params
112+
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
113+
GIDConfiguration *configuration = [self configurationWithClientIdArgument:params.clientId
114+
serverClientIdArgument:params.serverClientId
115+
hostedDomainArgument:params.hostedDomain];
116+
if (configuration != nil) {
117+
self.requestedScopes = [NSSet setWithArray:params.scopes];
118+
self.configuration = configuration;
119+
} else {
120+
*error = [FlutterError errorWithCode:@"missing-config"
122121
message:@"GoogleService-Info.plist file not found and clientId "
123122
@"was not provided programmatically."
124-
details:nil]);
125-
}
126-
} else if ([call.method isEqualToString:@"signInSilently"]) {
127-
[self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) {
128-
[self didSignInForUser:user result:result withError:error];
129-
}];
130-
} else if ([call.method isEqualToString:@"isSignedIn"]) {
131-
result(@([self.signIn hasPreviousSignIn]));
132-
} else if ([call.method isEqualToString:@"signIn"]) {
133-
@try {
134-
GIDConfiguration *configuration = self.configuration
135-
?: [self configurationWithClientIdArgument:nil
136-
serverClientIdArgument:nil
137-
hostedDomainArgument:nil];
138-
[self.signIn signInWithConfiguration:configuration
139-
presentingViewController:[self topViewController]
140-
hint:nil
141-
additionalScopes:self.requestedScopes.allObjects
142-
callback:^(GIDGoogleUser *user, NSError *error) {
143-
[self didSignInForUser:user result:result withError:error];
144-
}];
145-
} @catch (NSException *e) {
146-
result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]);
147-
[e raise];
148-
}
149-
} else if ([call.method isEqualToString:@"getTokens"]) {
150-
GIDGoogleUser *currentUser = self.signIn.currentUser;
151-
GIDAuthentication *auth = currentUser.authentication;
152-
[auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) {
153-
result(error != nil ? getFlutterError(error) : @{
154-
@"idToken" : authentication.idToken,
155-
@"accessToken" : authentication.accessToken,
156-
});
157-
}];
158-
} else if ([call.method isEqualToString:@"signOut"]) {
159-
[self.signIn signOut];
160-
result(nil);
161-
} else if ([call.method isEqualToString:@"disconnect"]) {
162-
[self.signIn disconnectWithCallback:^(NSError *error) {
163-
[self respondWithAccount:@{} result:result error:nil];
164-
}];
165-
} else if ([call.method isEqualToString:@"requestScopes"]) {
166-
id scopeArgument = call.arguments[@"scopes"];
167-
if ([scopeArgument isKindOfClass:[NSArray class]]) {
168-
self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopeArgument];
169-
}
170-
NSSet<NSString *> *requestedScopes = self.requestedScopes;
171-
172-
@try {
173-
[self.signIn addScopes:requestedScopes.allObjects
174-
presentingViewController:[self topViewController]
175-
callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) {
176-
if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] &&
177-
addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) {
178-
result([FlutterError errorWithCode:@"sign_in_required"
179-
message:@"No account to grant scopes."
180-
details:nil]);
181-
} else if ([addedScopeError.domain
182-
isEqualToString:kGIDSignInErrorDomain] &&
183-
addedScopeError.code ==
184-
kGIDSignInErrorCodeScopesAlreadyGranted) {
185-
// Scopes already granted, report success.
186-
result(@YES);
187-
} else if (addedScopeUser == nil) {
188-
result(@NO);
189-
} else {
190-
NSSet<NSString *> *grantedScopes =
191-
[NSSet setWithArray:addedScopeUser.grantedScopes];
192-
BOOL granted = [requestedScopes isSubsetOfSet:grantedScopes];
193-
result(@(granted));
194-
}
195-
}];
196-
} @catch (NSException *e) {
197-
result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]);
198-
}
199-
} else {
200-
result(FlutterMethodNotImplemented);
123+
details:nil];
201124
}
202125
}
203126

204-
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
205-
return [self.signIn handleURL:url];
127+
- (void)signInSilentlyWithCompletion:(nonnull void (^)(FSIUserData *_Nullable,
128+
FlutterError *_Nullable))completion {
129+
[self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) {
130+
[self didSignInForUser:user withCompletion:completion error:error];
131+
}];
132+
}
133+
134+
- (nullable NSNumber *)isSignedInWithError:
135+
(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
136+
return @([self.signIn hasPreviousSignIn]);
137+
}
138+
139+
- (void)signInWithCompletion:(nonnull void (^)(FSIUserData *_Nullable,
140+
FlutterError *_Nullable))completion {
141+
@try {
142+
GIDConfiguration *configuration = self.configuration
143+
?: [self configurationWithClientIdArgument:nil
144+
serverClientIdArgument:nil
145+
hostedDomainArgument:nil];
146+
[self.signIn signInWithConfiguration:configuration
147+
presentingViewController:[self topViewController]
148+
hint:nil
149+
additionalScopes:self.requestedScopes.allObjects
150+
callback:^(GIDGoogleUser *user, NSError *error) {
151+
[self didSignInForUser:user
152+
withCompletion:completion
153+
error:error];
154+
}];
155+
} @catch (NSException *e) {
156+
completion(nil, [FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]);
157+
[e raise];
158+
}
159+
}
160+
161+
- (void)getAccessTokenWithCompletion:(nonnull void (^)(FSITokenData *_Nullable,
162+
FlutterError *_Nullable))completion {
163+
GIDGoogleUser *currentUser = self.signIn.currentUser;
164+
GIDAuthentication *auth = currentUser.authentication;
165+
[auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) {
166+
if (error) {
167+
completion(nil, getFlutterError(error));
168+
} else {
169+
completion([FSITokenData makeWithIdToken:authentication.idToken
170+
accessToken:authentication.accessToken],
171+
nil);
172+
}
173+
}];
174+
}
175+
176+
- (void)signOutWithError:(FlutterError *_Nullable *_Nonnull)error;
177+
{ [self.signIn signOut]; }
178+
179+
- (void)disconnectWithCompletion:(nonnull void (^)(FlutterError *_Nullable))completion {
180+
[self.signIn disconnectWithCallback:^(NSError *error) {
181+
// TODO(stuartmorgan): This preserves the pre-Pigeon-migration behavior, but it's unclear why
182+
// 'error' is being ignored here.
183+
completion(nil);
184+
}];
185+
}
186+
187+
- (void)requestScopes:(nonnull NSArray<NSString *> *)scopes
188+
completion:(nonnull void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
189+
self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopes];
190+
NSSet<NSString *> *requestedScopes = self.requestedScopes;
191+
192+
@try {
193+
[self.signIn addScopes:requestedScopes.allObjects
194+
presentingViewController:[self topViewController]
195+
callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) {
196+
BOOL granted = NO;
197+
FlutterError *error = nil;
198+
if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] &&
199+
addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) {
200+
error = [FlutterError errorWithCode:@"sign_in_required"
201+
message:@"No account to grant scopes."
202+
details:nil];
203+
} else if ([addedScopeError.domain
204+
isEqualToString:kGIDSignInErrorDomain] &&
205+
addedScopeError.code ==
206+
kGIDSignInErrorCodeScopesAlreadyGranted) {
207+
// Scopes already granted, report success.
208+
granted = YES;
209+
} else if (addedScopeUser == nil) {
210+
granted = NO;
211+
} else {
212+
NSSet<NSString *> *grantedScopes =
213+
[NSSet setWithArray:addedScopeUser.grantedScopes];
214+
granted = [requestedScopes isSubsetOfSet:grantedScopes];
215+
}
216+
completion(error == nil ? @(granted) : nil, error);
217+
}];
218+
} @catch (NSException *e) {
219+
completion(nil, [FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]);
220+
}
206221
}
207222

208223
#pragma mark - <GIDSignInUIDelegate> protocol
@@ -250,35 +265,27 @@ - (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg
250265
}
251266

252267
- (void)didSignInForUser:(GIDGoogleUser *)user
253-
result:(FlutterResult)result
254-
withError:(NSError *)error {
268+
withCompletion:(nonnull void (^)(FSIUserData *_Nullable,
269+
FlutterError *_Nullable))completion
270+
error:(NSError *)error {
255271
if (error != nil) {
256272
// Forward all errors and let Dart side decide how to handle.
257-
[self respondWithAccount:nil result:result error:error];
273+
completion(nil, getFlutterError(error));
258274
} else {
259275
NSURL *photoUrl;
260276
if (user.profile.hasImage) {
261277
// Placeholder that will be replaced by on the Dart side based on screen size.
262278
photoUrl = [user.profile imageURLWithDimension:1337];
263279
}
264-
[self respondWithAccount:@{
265-
@"displayName" : user.profile.name ?: [NSNull null],
266-
@"email" : user.profile.email ?: [NSNull null],
267-
@"id" : user.userID ?: [NSNull null],
268-
@"photoUrl" : [photoUrl absoluteString] ?: [NSNull null],
269-
@"serverAuthCode" : user.serverAuthCode ?: [NSNull null]
270-
}
271-
result:result
272-
error:nil];
280+
completion([FSIUserData makeWithDisplayName:user.profile.name
281+
email:user.profile.email
282+
userId:user.userID
283+
photoUrl:[photoUrl absoluteString]
284+
serverAuthCode:user.serverAuthCode],
285+
nil);
273286
}
274287
}
275288

276-
- (void)respondWithAccount:(NSDictionary<NSString *, id> *)account
277-
result:(FlutterResult)result
278-
error:(NSError *)error {
279-
result(error != nil ? getFlutterError(error) : account);
280-
}
281-
282289
- (UIViewController *)topViewController {
283290
#pragma clang diagnostic push
284291
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -316,4 +323,5 @@ - (UIViewController *)topViewControllerFromViewController:(UIViewController *)vi
316323
}
317324
return viewController;
318325
}
326+
319327
@end

0 commit comments

Comments
 (0)