Chapters

Hide chapters

watchOS With SwiftUI by Tutorials

First Edition · watchOS 8 · Swift 5.5 · Xcode 13.1

Section I: watchOS With SwiftUI

Section 1: 16 chapters
Show chapters Hide chapters

14. Sign in With Apple
Written by Scott Grosch

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Sign in with Apple, or SIWA, has been around for a few years. Your customers will greatly appreciate the simplicity of registration and authentication when using a device with a screen as small as the Apple Watch.

You should consider this a bonus chapter, as there’s nothing Apple Watch-specific related to SIWA. If you’re already familiar with how to implement SIWA, feel free to skip this chapter. Due to the extra importance of a streamlined experience on the Apple Watch, I felt it made sense to include this content in the book.

Note: To complete this chapter, you’ll need a paid Apple developer account to set up the required identifiers and profiles.

This chapter only deals with the watchOS parts of SIWA. Embedded Sign in with Apple JS, web server and developer portal configurations are outside the scope of this book.

Some vendors won’t implement SIWA due to the anonymization of email addresses. When a customer emails the vendor with a support ticket, it’s harder to locate the customer’s account. However, you should keep in mind that your user’s experience is what you should prioritize, not yours.

To authenticate or not

Build and run the SignIn app from this chapter’s starter materials. You’ll see a simple screen that lets you choose which of two views to display. Tap the top button, and you’re taken to the part of the app which doesn’t require authentication. However, tap the second button, and you’re asked to authenticate:

Authenticate via username and password

The authentication screen you currently see is what most apps present: a simple username and password entry view.

TextField("User Name", text: $userName)
  .textContentType(.username)

SecureField("Password", text: $password)
  .textContentType(.password)
private var isButtonDisabled: Bool {
  return userName.trimmingCharacters(in: .whitespaces).isEmpty
    || password.isEmpty
}
Button("Sign In", action: signInTapped)
  .disabled(isButtonDisabled)

Handling sign in

Now switch to Authenticating/SignInView.swift. The SignInView is where you handle the how of your app’s login. The first piece you’ll notice is the use of app storage:

@AppStorage("userName") private var storedUserName = ""
private func userPasswordCompletion(userName: String, password: String) {
  // 1
  storedUserName = userName
  // 2
  var request = URLRequest(
    url: URL(string: "https://your.site.com/login")!
  )
  request.httpMethod = "POST"
  // request.httpBody = ...

  DispatchQueue.main.async {
    // 3
    onSignedIn("some token here")
    // 4
    dismiss()
  }
}

Signing in with Apple

At this point, your login screen is fully functional. Functional, but not friendly. I don’t know about you, but I always struggle to draw letters exactly as the Apple Watch wants them. Wouldn’t it be better to have a Sign in with Apple button available?

Adding the SIWA button

While still editing SignInView.swift, add an import to the top of the file:

import AuthenticationServices
private func onRequest(request: ASAuthorizationAppleIDRequest) {
}

private func siwaCompletion(result: Result<ASAuthorization, Error>) {
}
SignInWithAppleButton(
  onRequest: onRequest,
  onCompletion: siwaCompletion
)
  .signInWithAppleButtonStyle(.white)

Divider()
  .padding()

Configuring the request

You’ll configure the request in the onRequest(request:) stub you generated. Add the following code there:

request.requestedScopes = [.fullName]
request.state = "some state string"
private let nonce = UUID().uuidString
request.nonce = nonce

Handling the reply

When you get a reply back from Apple, there are three possible outcomes:

guard
  // 1
  case .success(let authorization) = result,
  // 2
  let credential = authorization.credential as? ASAuthorizationAppleIDCredential
else {
  // 3
  if case .failure(let error) = result {
    print("Failed to authenticate: \(error.localizedDescription)")
  }

  return
}
if credential.fullName == nil {
  // You've logged in an existing user account.
} else {
  // The user does not exist, so register a new account.
}

Storing the user credentials

The user property of the credential contains the unique identifier that Apple assigned to your user. You’ll likely want to pass that identifier to all the calls you send to your web server. A simple way to handle that requirement is to add another @AppStorage to the top of SignInView:

@AppStorage("userCredential") private var userCredential = ""
userCredential = credential.user

Storing their name properly

If you’ve requested the user’s full name, keep in mind that the value provided is a PersonNameComponents, not a String. To display the name, use the appropriate formatter, like so:

PersonNameComponentsFormatter.localizedString(
  from: name,
  style: .default
)
let me = credential.fullName
let badNameExample = "\(me.givenName) \(me.familyName)"
let encoder = JSONEncoder()
guard let data = try? encoder.encode(credential.fullName) else {
  // handle error appropriately
  return
}

let encoded = data.base64EncodedString()
let decoder = JSONDecoder()

guard
  let data = Data(base64Encoded: encoded),
  let components = try? decoder.decode(
    PersonNameComponents.self,
    from: data
  )
else {
  // handle error appropriately
  return
}

let name = PersonNameComponentsFormatter.localizedString(
  from: components,
  style: .short
)

Key points

  • Adding SIWA is relatively easy, so use it wherever possible.
  • Ensure that you store new user registration details local to the device until your app’s web server performs a successful registration.
  • Only request the user’s name and email address if you truly need them.
  • Keep in mind that most email addresses you receive will be an Apple relay address.
  • Always store the full PersonNameComponentsFormatter details on your web server.

Where to go from here?

For full details on SIWA, please see Apple’s document, Sign in with Apple.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now