Example of oAuth authentication with multi-account support on iOS using GoogleSignIn
and GTMAppAuth
.
Reason we're using both libraries is because GoogleSignIn
is Google's new preferred way of doing oAuth on iOS but it doesn't support multiple accounts to be logged in at the same time. To make that work we use a few methods from GTMAppAuth
to save and load authorization objects to and from the keychain.
- Create an oAuth client in Google's API Console
- Open
google-oauth-example.xcworkspace
in xCode (note that this project only supports iOS v15+ and therefor xCode v13+) - Add the reversed client ID to your URL Types
- Go to
GoogleAuth.swift
and replaceYOUR_OAUTH_CLIENT_ID
with your newly created client ID - Run the project in a simulator of your choice
- Load all saved authorizations from your keychain
GoogleAuth.shared.loadSavedAuthorizations() {
print("loaded authorizations")
}
- For new sign ins use
GoogleAuth.shared.signIn(_:)
GoogleAuth.shared.signIn() {
success in
print(success ? "signed in" : "sign in failed")
}
- Handle incoming URLs for when Google redirects back to your app after login
extension SceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
GoogleAuth.shared.handleUrl(url)
}
}
- Observe
GoogleAuth.shared
for changes in the logged in accounts
struct AccountsList: View {
@ObservedObject var googleAuth = GoogleAuth.shared
var body: some View {
ForEach(googleAuth.profiles, id: \.self.email) {
profile in
Text(profile.email)
}
}
}
- Log out accounts using their profile sub
GoogleAuth.shared.logOut(profile.sub)
- Use
GoogleApi.shared.fetch(url:authorization:dataStructure:_:)
for your own API requests
guard let profileSub = GoogleAuth.shared.profiles.first?.sub else { return }
guard let authorization = GoogleAuth.shared.authorizations[profileSub] else { return }
guard let url = URL(string: "https://www.googleapis.com/oauth2/v3/userinfo") else { return }
GoogleApi.shared.fetch(
url: url,
authorization: authorization,
dataStructure: GoogleProfile.self
) {
profile, error in
guard error == nil else { return }
guard let profile = profile else { return }
print(profile)
}