Skip to content

[google_sign_in] PR 4/4 Convert the Pigeon host API from Objective-C to Swift - #12658

Open
victogomez-cs wants to merge 6 commits into
pr3/google-sign-in-ios-view-providerfrom
pr4/google-sign-in-ios-swift-pigeon
Open

[google_sign_in] PR 4/4 Convert the Pigeon host API from Objective-C to Swift#12658
victogomez-cs wants to merge 6 commits into
pr3/google-sign-in-ios-view-providerfrom
pr4/google-sign-in-ios-swift-pigeon

Conversation

@victogomez-cs

@victogomez-cs victogomez-cs commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Converts the Pigeon host API from Objective-C to Swift (swiftOut / messages.g.swift) and implements GoogleSignInApi with throws/Result instead of Obj-C error pointers.

Generated Swift is committed codegen from pigeons/messages.dart (Pigeon 26.3.4), then swift-format. messages.g.h / messages.g.m are removed. The Obj-C SPM target’s publicHeadersPath is narrowed to the remaining Obj-C headers.

Missing-presenter failures from PR 3/4 are returned as PigeonError. Tests updated for the Swift Pigeon types.

Bumps google_sign_in_ios to 6.3.5.

PR 4/4 of the Obj-C → Swift migration. Depends on PR 3/4 (ViewProvider / GID wrappers). Completes flutter/flutter#119103 for the iOS/macOS plugin implementation.

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Aug 27, 2026
@victogomez-cs victogomez-cs added triage-ios Should be looked at in iOS triage and removed CICD Run CI/CD labels Aug 27, 2026
@google-cla

google-cla Bot commented Aug 27, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request migrates the Pigeon host API for the iOS Google Sign-In plugin from Objective-C to Swift, replacing the autogenerated Objective-C files with Swift files and updating the plugin implementation and tests accordingly. Feedback on the changes suggests using the any keyword for existential protocol types to align with modern Swift standards, replacing an Objective-C style string formatting initializer with a simple string literal, and avoiding force-casting in tests to prevent potential test runner crashes.

Comment on lines +102 to +105
let signIn: GIDSignInProtocol

/// A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls.
var usersByIdentifier: [String: any GIDGoogleUserProtocol] = [:]
var usersByIdentifier: [String: GIDGoogleUserProtocol] = [:]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To maintain consistency with the rest of the codebase (such as GoogleSignInTests.swift) and ensure readiness for Swift 6's strict existential type requirements, please use the any keyword when referring to the existential protocol types GIDSignInProtocol and GIDGoogleUserProtocol.

Suggested change
let signIn: GIDSignInProtocol
/// A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls.
var usersByIdentifier: [String: any GIDGoogleUserProtocol] = [:]
var usersByIdentifier: [String: GIDGoogleUserProtocol] = [:]
let signIn: any GIDSignInProtocol
/// A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls.
var usersByIdentifier: [String: any GIDGoogleUserProtocol] = [:]
References
  1. Code should follow the relevant style guides and language idioms for Swift, which includes using existential any for protocol types to align with modern Swift standards and prepare for Swift 6. (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Marked the GIDSignInProtocol / GIDGoogleUserProtocol existentials with any on the stored properties and the matching init and method parameters

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment seems valid?

completion(
.failure(
PigeonError(
code: String(format: "%@: %ld", "(null)", 0),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

In Swift, we can use a simple string literal "(null): 0" instead of the Objective-C style String(format:) initializer for better readability and performance.

Suggested change
code: String(format: "%@: %ld", "(null)", 0),
code: "(null): 0",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, those format arguments were constants, so the literal is equivalent. Updated to "(null): 0"

Comment on lines +537 to +540
// Unexpected errors, such as runtime exceptions, are returned as
// FlutterError.
let flutterError = error as! PigeonError
#expect(flutterError.code == "google_sign_in")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

In Swift Testing, using force-casting (as!) can cause the entire test runner to crash if the cast fails, preventing other tests from running. Instead, use try #require(error as? PigeonError) to fail the test gracefully. Additionally, update the comment to refer to PigeonError instead of FlutterError since the API has been migrated. Note that this same pattern of using as! and outdated FlutterError references occurs in several other places in this file (e.g., lines 639, 732, 755, 854, 886, 945) and should be updated similarly.

Suggested change
// Unexpected errors, such as runtime exceptions, are returned as
// FlutterError.
let flutterError = error as! PigeonError
#expect(flutterError.code == "google_sign_in")
// Unexpected errors, such as runtime exceptions, are returned as
// PigeonError.
let pigeonError = try #require(error as? PigeonError)
#expect(pigeonError.code == "google_sign_in")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated the comments to PigeonError and dropped the as! casts so a type mismatch fails the test instead of crashing the runner.

I didn’t use try #require here: these assertions sit in a non-throwing completion, so try doesn’t compile, and a thrown #require would skip confirmed() and hang the test. Used guard let … as? PigeonError so confirmed() still runs

@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from 661e862 to 0e55e56 Compare August 27, 2026 17:37
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch 2 times, most recently from 199a0af to 5b3567d Compare August 27, 2026 20:56
@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from b2aa6db to f89ebdd Compare August 28, 2026 17:22
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from 2413466 to 287290e Compare August 28, 2026 17:23

@LongCatIsLooong LongCatIsLooong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM with nits.

return PigeonError(
code: "\(error.domain): \(error.code)",
message: error.localizedDescription,
details: sanitizedUserInfo(error.userInfo) as Sendable)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: the up casting part looks slightly weird. I searched for the call sites in this PR and in all but one cases we're calling this on NSError.userInfo. Would it be possible to make this helper function return a more specialized type like Dictionary<String, any Sendable>, or create a NSError.sanitizedUserInfo getter via an extension instead of exposing this helper function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, the upcast was only there because the helper returned Any. I moved this onto NSError.sanitizedUserInfo as [String: any Sendable], so PigeonError.details can take it without casting. The recursive sanitizer is now an implementation detail of that getter

Comment on lines +102 to +105
let signIn: GIDSignInProtocol

/// A mapping of user IDs to GIDGoogleUser instances to use for follow-up calls.
var usersByIdentifier: [String: any GIDGoogleUserProtocol] = [:]
var usersByIdentifier: [String: GIDGoogleUserProtocol] = [:]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment seems valid?

withParameters params: FSIPlatformConfigurationParams,
error: AutoreleasingUnsafeMutablePointer<FlutterError?>
) {
func configure(params: PlatformConfigurationParams) throws {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm why the access level change, are these not public APIs? Now the configure method is going to have an internal access level if I recall correctly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah they don't have to be public since they're only consumed by the pigeon generated messages.g.swift which lives in the same module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, they don't have to be public. configure (and the rest of GoogleSignInApi) is only invoked from generated messages.g.swift in this same module, plus tests, so internal is the right Swift access level

PigeonError(
code: "(null): 0",
message: nil,
details: sanitizedUserInfo(nil) as Sendable)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So details would be [Unsupported type: nil]? That doesn't feel like a helpful error message for either plugin users or app users.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This branch is not supposed to be reachable is it? user and error can't both be nil?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right on both counts. That branch is a defensive fallback for an unexpected GID completion (neither user nor error); it shouldn't have been reporting a codec placeholder.

It now returns a PigeonError with code google_sign_in and message Sign-in completed without a user or an error., and details is nil. Added a test that hits this via restorePreviousSignIn with no fake user/error

@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch 2 times, most recently from 311042e to 88ccb96 Compare September 2, 2026 17:55
@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from 48c44fb to 3832e69 Compare September 2, 2026 18:43
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from 88ccb96 to e7cb239 Compare September 2, 2026 18:43
@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from 3832e69 to a183912 Compare September 2, 2026 21:13
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from e7cb239 to 924862a Compare September 2, 2026 21:13
@LongCatIsLooong

Copy link
Copy Markdown
Contributor

Unrelated: just noticed the PR branches are from this repo instead of a fork. Do you know if PR stacking work when the source branches are from a fork? I was trying out this myself but couldn't get that to work in flutter/flutter.

@victogomez-cs

Copy link
Copy Markdown
Contributor Author

Unrelated: just noticed the PR branches are from this repo instead of a fork. Do you know if PR stacking work when the source branches are from a fork? I was trying out this myself but couldn't get that to work in flutter/flutter.

GitHub's native stacked PRs require all branches in the stack to be in the same repository, cross-fork stacks aren't supported. So if your feature branches live on a fork, you can't use the native stack feature against the upstream repo
@LongCatIsLooong

@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from a183912 to bc6b4a3 Compare September 3, 2026 18:27
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from 924862a to 5b44f2c Compare September 3, 2026 18:27

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would strongly recommend resolving this as part of this PR. Making SignInResult an empty sealed class, and having SignInResult and SignInFailure extends it will make code simpler and safer because the type system will enforce things that are currently enforced only by the convention that exactly one field should be non-null.

The only reason that this wasn't part of the initial API is that it requires Swift Pigeon generation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. SignInResult is now an empty sealed class, with SignInSuccess and SignInFailure as the subclasses, same shape as Android’s GetCredentialResult. Dart uses exhaustive switches instead of the “exactly one field is non-null” convention, and Swift returns SignInSuccess / SignInFailure directly. Could you take a look at the last commit (fa637144)?

@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from bc6b4a3 to 878c0f9 Compare September 3, 2026 18:53
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from 5b44f2c to 733952e Compare September 3, 2026 18:54
@LongCatIsLooong

Copy link
Copy Markdown
Contributor

(From triage: this can't land or be tested on CI until it is rebased on main. Do you plan to land the PR sequence in order?)

@victogomez-cs

victogomez-cs commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

(From triage: this can't land or be tested on CI until it is rebased on main. Do you plan to land the PR sequence in order?)

Yes, they need to land in order, but reviews can happen in parallel. The first PR (1/4) is already merged; we're waiting on review of the second (2/4) so we can continue the sequence. This repo doesn't support stacked PRs yet, so this one can't be retargeted onto main until 12555 and 12657 land.
@LongCatIsLooong

@victogomez-cs
victogomez-cs force-pushed the pr3/google-sign-in-ios-view-provider branch from 878c0f9 to bf080a0 Compare September 4, 2026 15:54
… for missing presenter.

Refactors the sign-in method to handle errors more robustly by switching to a result-based approach. This change ensures that when no presenter is available, a PigeonError is returned, allowing for better error reporting in the Google Sign-In process. Updates existing tests to verify the new error handling behavior.
… add tests for sign-in restoration.

Refactors the error handling mechanism to utilize a more structured approach with PigeonError, particularly for cases where sign-in completes without a user or error. Introduces a new test to validate this behavior, ensuring robust error reporting and improved clarity in the sign-in process.
…e related tests.

Converts the SignInResult structure to a sealed class with distinct SignInSuccess and SignInFailure subclasses, enhancing type safety and clarity. Updates the GoogleSignInPlugin and associated tests to utilize the new structure, ensuring robust handling of sign-in results and errors.
@victogomez-cs
victogomez-cs force-pushed the pr4/google-sign-in-ios-swift-pigeon branch from fa63714 to d211854 Compare September 4, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants