In this video we’re going to add Sign in with Apple to your iOS app and learn how to show the button on the login screen. Please note that you’ll need an Apple developer account to test this out.
Adding the capability
Open the TILiOS project in Xcode and navigate to the TILiOS target. Change the bundle identifier to something unique to your project. For example, this video will use dev.timc.siwa-demo.TILiOS.
Next click on the Signing & Capabilities tab and select your development team to ensure the app is signed correctly.
Finally, click on the add new capability button, search for Sign in with Apple and select it. This allows you to use Sign in with Apple in your app.
Displaying the button
Now that you can do so, it’s time to show the Sign in with Apple button on the log in screen. Open LoginView.swift and at the top of the file, import AuthenticationServices:
import AuthenticationServices
AuthenticationServices provides a built in view for the Sign in with Apple button. Insert it below the AsyncButton for Log In, using the signIn label:
SignInWithAppleButton(.signIn) { request in
} onCompletion: { result in
}
Then, in the request closure, you need to specify the scopes. You want to request full name and email to be able to register the user with the Vapor app:
request.requestedScopes = [.fullName, .email]
Next, create a new function called handleSIWA that takes a result and returns a Token. Annotate the function with @MainActor:
@MainActor
func handleSIWA(result: Result<ASAuthorization, Error>) async throws -> Token {
}
Inside, switch on the result and if there’s an error, display the error dialogue, print the error and throw it:
switch result {
case .failure(let error):
self.showingLoginErrorAlert = true
print("Error \(error)")
throw error
}
The success case gets an ASAuthorization from the result. Add that case and ensure you can get the credential, otherwise, throw an error:
case .success(let authResult):
if let credential = authResult.credential as? ASAuthorizationAppleIDCredential {
} else {
self.showingLoginErrorAlert = true
throw AuthError.badResponse
}
If there are credentials, extract the JWT for the identity as a String and the name:
guard
let identityToken = credential.identityToken,
let tokenString = String(data: identityToken, encoding: .utf8)
else {
print("Failed to get token from credential")
self.showingLoginErrorAlert = true
throw AuthError.badResponse
}
let name: String?
if let nameProvided = credential.fullName {
name = "\(nameProvided.givenName ?? "") \(nameProvided.familyName ?? "")"
} else {
name = nil
}
Then, throw an error to allow the function to compile:
throw AuthError.notLoggedIn
Finally, hook up the Sign in with Apple button with the handleSIWA function. In the completion handler, create a new Task to allow the async function to execute and get the token, even though that’s not implemented yet:
Task {
let apiToken = try await handleSIWA(result: result)
}
Once you have the token, assign it to the Auth EnvironmentObject:
auth.token = apiToken.value
This triggers a refresh of the app and marks the user as signed in. Now users can trigger the Sign in with Apple flow when logging in to the app. Build the app to make sure everything compiles.