[google_sign_in] PR 4/4 Convert the Pigeon host API from Objective-C to Swift - #12658
[google_sign_in] PR 4/4 Convert the Pigeon host API from Objective-C to Swift#12658victogomez-cs wants to merge 6 commits into
Conversation
|
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. |
There was a problem hiding this comment.
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.
| 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] = [:] |
There was a problem hiding this comment.
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.
| 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
- Code should follow the relevant style guides and language idioms for Swift, which includes using existential
anyfor protocol types to align with modern Swift standards and prepare for Swift 6. (link)
There was a problem hiding this comment.
Done. Marked the GIDSignInProtocol / GIDGoogleUserProtocol existentials with any on the stored properties and the matching init and method parameters
There was a problem hiding this comment.
This comment seems valid?
| completion( | ||
| .failure( | ||
| PigeonError( | ||
| code: String(format: "%@: %ld", "(null)", 0), |
There was a problem hiding this comment.
Agreed, those format arguments were constants, so the literal is equivalent. Updated to "(null): 0"
| // Unexpected errors, such as runtime exceptions, are returned as | ||
| // FlutterError. | ||
| let flutterError = error as! PigeonError | ||
| #expect(flutterError.code == "google_sign_in") |
There was a problem hiding this comment.
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.
| // 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") |
There was a problem hiding this comment.
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
661e862 to
0e55e56
Compare
199a0af to
5b3567d
Compare
b2aa6db to
f89ebdd
Compare
2413466 to
287290e
Compare
| return PigeonError( | ||
| code: "\(error.domain): \(error.code)", | ||
| message: error.localizedDescription, | ||
| details: sanitizedUserInfo(error.userInfo) as Sendable) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
| 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] = [:] |
There was a problem hiding this comment.
This comment seems valid?
| withParameters params: FSIPlatformConfigurationParams, | ||
| error: AutoreleasingUnsafeMutablePointer<FlutterError?> | ||
| ) { | ||
| func configure(params: PlatformConfigurationParams) throws { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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))) |
There was a problem hiding this comment.
So details would be [Unsupported type: nil]? That doesn't feel like a helpful error message for either plugin users or app users.
There was a problem hiding this comment.
This branch is not supposed to be reachable is it? user and error can't both be nil?
There was a problem hiding this comment.
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
311042e to
88ccb96
Compare
48c44fb to
3832e69
Compare
88ccb96 to
e7cb239
Compare
3832e69 to
a183912
Compare
e7cb239 to
924862a
Compare
|
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 |
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 |
a183912 to
bc6b4a3
Compare
924862a to
5b44f2c
Compare
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)?
bc6b4a3 to
878c0f9
Compare
5b44f2c to
733952e
Compare
|
(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. |
878c0f9 to
bf080a0
Compare
… 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.
fa63714 to
d211854
Compare
Converts the Pigeon host API from Objective-C to Swift (
swiftOut/messages.g.swift) and implementsGoogleSignInApiwith throws/Resultinstead of Obj-C error pointers.Generated Swift is committed codegen from
pigeons/messages.dart(Pigeon 26.3.4), thenswift-format.messages.g.h/messages.g.mare removed. The Obj-C SPM target’spublicHeadersPathis 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_iosto 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
[shared_preferences]///).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-assistbot 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
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