Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[google_sign_in] Convert iOS to Pigeon #4941

Merged
merged 5 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/google_sign_in/google_sign_in_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 5.6.4

* Converts platform communication to Pigeon.

## 5.6.3

* Adds pub topics to package metadata.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@

#import <Flutter/Flutter.h>

@interface FLTGoogleSignInPlugin : NSObject <FlutterPlugin>
#import "messages.g.h"

@interface FLTGoogleSignInPlugin : NSObject <FlutterPlugin, FSIGoogleSignInApi>
@end
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,9 @@ - (instancetype)init;
@implementation FLTGoogleSignInPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel =
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in_ios"
binaryMessenger:[registrar messenger]];
FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] init];
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
FSIGoogleSignInApiSetup(registrar.messenger, instance);
}

- (instancetype)init {
Expand Down Expand Up @@ -105,104 +102,122 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn

#pragma mark - <FlutterPlugin> protocol

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([call.method isEqualToString:@"init"]) {
GIDConfiguration *configuration =
[self configurationWithClientIdArgument:call.arguments[@"clientId"]
serverClientIdArgument:call.arguments[@"serverClientId"]
hostedDomainArgument:call.arguments[@"hostedDomain"]];
if (configuration != nil) {
if ([call.arguments[@"scopes"] isKindOfClass:[NSArray class]]) {
self.requestedScopes = [NSSet setWithArray:call.arguments[@"scopes"]];
}
self.configuration = configuration;
result(nil);
} else {
result([FlutterError errorWithCode:@"missing-config"
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
return [self.signIn handleURL:url];
}

#pragma mark - FSIGoogleSignInApi

- (void)initializeSignInWithParameters:(nonnull FSIInitParams *)params
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
GIDConfiguration *configuration = [self configurationWithClientIdArgument:params.clientId
serverClientIdArgument:params.serverClientId
hostedDomainArgument:params.hostedDomain];
if (configuration != nil) {
self.requestedScopes = [NSSet setWithArray:params.scopes];
self.configuration = configuration;
} else {
*error = [FlutterError errorWithCode:@"missing-config"
message:@"GoogleService-Info.plist file not found and clientId "
@"was not provided programmatically."
details:nil]);
}
} else if ([call.method isEqualToString:@"signInSilently"]) {
[self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) {
[self didSignInForUser:user result:result withError:error];
}];
} else if ([call.method isEqualToString:@"isSignedIn"]) {
result(@([self.signIn hasPreviousSignIn]));
} else if ([call.method isEqualToString:@"signIn"]) {
@try {
GIDConfiguration *configuration = self.configuration
?: [self configurationWithClientIdArgument:nil
serverClientIdArgument:nil
hostedDomainArgument:nil];
[self.signIn signInWithConfiguration:configuration
presentingViewController:[self topViewController]
hint:nil
additionalScopes:self.requestedScopes.allObjects
callback:^(GIDGoogleUser *user, NSError *error) {
[self didSignInForUser:user result:result withError:error];
}];
} @catch (NSException *e) {
result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]);
[e raise];
}
} else if ([call.method isEqualToString:@"getTokens"]) {
GIDGoogleUser *currentUser = self.signIn.currentUser;
GIDAuthentication *auth = currentUser.authentication;
[auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) {
result(error != nil ? getFlutterError(error) : @{
@"idToken" : authentication.idToken,
@"accessToken" : authentication.accessToken,
});
}];
} else if ([call.method isEqualToString:@"signOut"]) {
[self.signIn signOut];
result(nil);
} else if ([call.method isEqualToString:@"disconnect"]) {
[self.signIn disconnectWithCallback:^(NSError *error) {
[self respondWithAccount:@{} result:result error:nil];
}];
} else if ([call.method isEqualToString:@"requestScopes"]) {
id scopeArgument = call.arguments[@"scopes"];
if ([scopeArgument isKindOfClass:[NSArray class]]) {
self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopeArgument];
}
NSSet<NSString *> *requestedScopes = self.requestedScopes;

@try {
[self.signIn addScopes:requestedScopes.allObjects
presentingViewController:[self topViewController]
callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) {
if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] &&
addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) {
result([FlutterError errorWithCode:@"sign_in_required"
message:@"No account to grant scopes."
details:nil]);
} else if ([addedScopeError.domain
isEqualToString:kGIDSignInErrorDomain] &&
addedScopeError.code ==
kGIDSignInErrorCodeScopesAlreadyGranted) {
// Scopes already granted, report success.
result(@YES);
} else if (addedScopeUser == nil) {
result(@NO);
} else {
NSSet<NSString *> *grantedScopes =
[NSSet setWithArray:addedScopeUser.grantedScopes];
BOOL granted = [requestedScopes isSubsetOfSet:grantedScopes];
result(@(granted));
}
}];
} @catch (NSException *e) {
result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]);
}
} else {
result(FlutterMethodNotImplemented);
details:nil];
}
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
return [self.signIn handleURL:url];
- (void)signInSilentlyWithCompletion:(nonnull void (^)(FSIUserData *_Nullable,
FlutterError *_Nullable))completion {
[self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) {
[self didSignInForUser:user withCompletion:completion error:error];
}];
}

- (nullable NSNumber *)isSignedInWithError:
(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
return @([self.signIn hasPreviousSignIn]);
}

- (void)signInWithCompletion:(nonnull void (^)(FSIUserData *_Nullable,
FlutterError *_Nullable))completion {
@try {
GIDConfiguration *configuration = self.configuration
?: [self configurationWithClientIdArgument:nil
serverClientIdArgument:nil
hostedDomainArgument:nil];
[self.signIn signInWithConfiguration:configuration
presentingViewController:[self topViewController]
hint:nil
additionalScopes:self.requestedScopes.allObjects
callback:^(GIDGoogleUser *user, NSError *error) {
[self didSignInForUser:user
withCompletion:completion
error:error];
}];
} @catch (NSException *e) {
completion(nil, [FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]);
[e raise];
}
}

- (void)getAccessTokenWithCompletion:(nonnull void (^)(FSITokenData *_Nullable,
FlutterError *_Nullable))completion {
GIDGoogleUser *currentUser = self.signIn.currentUser;
GIDAuthentication *auth = currentUser.authentication;
[auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) {
if (error) {
completion(nil, getFlutterError(error));
} else {
completion([FSITokenData makeWithIdToken:authentication.idToken
accessToken:authentication.accessToken],
nil);
}
}];
}

- (void)signOutWithError:(FlutterError *_Nullable *_Nonnull)error;
{ [self.signIn signOut]; }

- (void)disconnectWithCompletion:(nonnull void (^)(FlutterError *_Nullable))completion {
[self.signIn disconnectWithCallback:^(NSError *error) {
// TODO(stuartmorgan): This preserves the pre-Pigeon-migration behavior, but it's unclear why
// 'error' is being ignored here.
completion(nil);
}];
}

- (void)requestScopes:(nonnull NSArray<NSString *> *)scopes
completion:(nonnull void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopes];
NSSet<NSString *> *requestedScopes = self.requestedScopes;

@try {
[self.signIn addScopes:requestedScopes.allObjects
presentingViewController:[self topViewController]
callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) {
BOOL granted = NO;
FlutterError *error = nil;
if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] &&
addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) {
error = [FlutterError errorWithCode:@"sign_in_required"
message:@"No account to grant scopes."
details:nil];
} else if ([addedScopeError.domain
isEqualToString:kGIDSignInErrorDomain] &&
addedScopeError.code ==
kGIDSignInErrorCodeScopesAlreadyGranted) {
// Scopes already granted, report success.
granted = YES;
} else if (addedScopeUser == nil) {
granted = NO;
} else {
NSSet<NSString *> *grantedScopes =
[NSSet setWithArray:addedScopeUser.grantedScopes];
granted = [requestedScopes isSubsetOfSet:grantedScopes];
}
completion(error == nil ? @(granted) : nil, error);
}];
} @catch (NSException *e) {
completion(nil, [FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diffs in this file look more extensive than they actually are because of indentation changes. The code here was made by:

  • creating empty stubs for each of the Pigeon protocol methods,
  • moving the body of the relevant if/else if block from handleMethodCall:result: into that method,
  • replacing arg dictionary lookups with the corresponding Pigeon argument (removing some now-unnecessary type-checking code), and
  • replacing result calls with completion calls.

}

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

- (void)didSignInForUser:(GIDGoogleUser *)user
result:(FlutterResult)result
withError:(NSError *)error {
withCompletion:(nonnull void (^)(FSIUserData *_Nullable,
FlutterError *_Nullable))completion
error:(NSError *)error {
if (error != nil) {
// Forward all errors and let Dart side decide how to handle.
[self respondWithAccount:nil result:result error:error];
completion(nil, getFlutterError(error));
} else {
NSURL *photoUrl;
if (user.profile.hasImage) {
// Placeholder that will be replaced by on the Dart side based on screen size.
photoUrl = [user.profile imageURLWithDimension:1337];
}
[self respondWithAccount:@{
@"displayName" : user.profile.name ?: [NSNull null],
@"email" : user.profile.email ?: [NSNull null],
@"id" : user.userID ?: [NSNull null],
@"photoUrl" : [photoUrl absoluteString] ?: [NSNull null],
@"serverAuthCode" : user.serverAuthCode ?: [NSNull null]
}
result:result
error:nil];
completion([FSIUserData makeWithDisplayName:user.profile.name
email:user.profile.email
userId:user.userID
photoUrl:[photoUrl absoluteString]
serverAuthCode:user.serverAuthCode],
nil);
}
}

- (void)respondWithAccount:(NSDictionary<NSString *, id> *)account
result:(FlutterResult)result
error:(NSError *)error {
result(error != nil ? getFlutterError(error) : account);
}

- (UIViewController *)topViewController {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Expand Down Expand Up @@ -316,4 +323,5 @@ - (UIViewController *)topViewControllerFromViewController:(UIViewController *)vi
}
return viewController;
}

@end
Loading