Sign in with Apple Using SwiftUI
Learn how to implement Sign in with Apple using SwiftUI, to give users more privacy and control in your iOS apps. By Scott Grosch.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Sign in with Apple Using SwiftUI
25 mins
- Getting Started
- Add Capabilities
- Add Sign In Button
- Handle Button Taps
- ASAuthorizationControllerDelegate
- Handling Registration
- Handling Existing Accounts
- Username and Password
- Finish Handling Button Press
- Automate Sign In
- Web Credentials
- Runtime Checks
- The UIWindow
- The Environment
- Environment Setup
- ContentView Changes
- Update the Delegate
- Update the Scene
- Logins Do not Scroll
- Where to Go From Here?
The Environment
A new concept with SwiftUI is the Environment. It’s an area wherein you can store data that needs to be available to many of your SwiftUI views. To some degree, you can think of it like dependency injection.
Take a look at EnvironmentWindowKey.swift, and you’ll see the code necessary to store a value in the SwiftUI environment. It’s boilerplate code wherein you define the key to pass to the @Environment
property wrapper and the value to be stored. Note how, since a class type is being stored, it explicitly marks the reference as weak
to prevent a retain cycle.
Jump back to ContentView.swift and add another property to the top of ContentView
:
@Environment(\.window) var window: UIWindow?
iOS will automatically populate the window
variable with the value stored in the environment.
In performSignIn(using:)
, modify the constructor call to pass the window property:
appleSignInDelegates = SignInWithAppleDelegates(window: window) { success in
You’ll also want to tell ASAuthorizationController
to use your class for the presentationContextProvider
delegate, so add this code right after assigning the controller.delegate
:
controller.presentationContextProvider = appleSignInDelegates
Open SignInWithAppleDelegates.swift to handle the new property and constructor changes to the class. Replace the class definition, but not the extension with all the registration and delegate methods, with the following:
class SignInWithAppleDelegates: NSObject {
private let signInSucceeded: (Bool) -> Void
// 1
private weak var window: UIWindow!
// 2
init(window: UIWindow?, onSignedIn: @escaping (Bool) -> Void) {
// 3
self.window = window
self.signInSucceeded = onSignedIn
}
}
Just a few changes:
- Store a new weak reference to the window.
- Add the
UIWindow
parameter as the first argument to the initializer. - Store the passed-in value to the property.
Finally, implement the new delegate type:
extension SignInWithAppleDelegates:
ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController)
-> ASPresentationAnchor {
return self.window
}
}
The delegate just has a single method to implement that is expected to return the window, which shows the Sign In with Apple modal dialog.
There’s just one piece left to getting the UIWindow
into the environment. Open SceneDelegate.swift and replace this line:
window.rootViewController = UIHostingController(rootView: ContentView())
With these lines:
// 1
let rootView = ContentView().environment(\.window, window)
// 2
window.rootViewController = UIHostingController(rootView: rootView)
Two small steps do it all:
- You create the
ContentView
and append the value of thewindow
variable. - You pass that
rootView
variable to theUIHostingController
instead of the old initializer.
The environment
method returns some View
which basically means it’s taking your ContentView
, shoving the value you pass into that view’s environment, and then returning the ContentView
back to you. Any SwiftUI View
which is presented from the ContentView
will now also hold that value in its environment.
If you create a new root view anywhere else, that root will not contain the environment values unless you explicitly pass them back in as well.
Logins Do not Scroll
One downside to Sign In with Apple to keep in mind is that the window that iOS displays will not scroll! For most users that won’t matter, but it’s important to note. As the owner of the site that my app uses, for example, I have numerous logins. Not only do I have the app login itself, but I’ve got a login for the SQL database, for the PHP admin site, etc.
If you’ve got too many logins, it’s possible end users won’t see what they actually need. Try to ensure that if you’re linking an app to a site that the site only has logins which will matter to the app. Don’t just bundle all your apps under a single domain.
Where to Go From Here?
You can download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
SignInWithAppleDelegates.swift currently returns a Boolean success, but you’ll likely want to use something more like Swift 5’s Result
type so that you can return not only data from your server, but also custom error types on failure. Please see our video course, What’s New in Swift 5: Types if you’re not familiar with the Result
type.
We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!