Implementing Face ID and Touch ID Authentication in SwiftUI

Securing applications with biometric authentication, such as Face ID and Touch ID, is becoming increasingly crucial. SwiftUI makes it relatively straightforward to implement these features. This guide will walk you through implementing Face ID and Touch ID authentication in a SwiftUI application.

What are Face ID and Touch ID?

  • Face ID: A facial recognition system developed by Apple, used for authentication and unlocking devices.
  • Touch ID: A fingerprint recognition system, also by Apple, used for similar purposes.

Why Use Biometric Authentication?

  • Enhanced Security: Biometrics provide a more secure way to authenticate users compared to traditional passwords or PINs.
  • Improved User Experience: Biometric authentication is faster and more convenient for users.
  • Wider Adoption: Users are becoming more accustomed to using biometrics for authentication, making it a standard expectation for security.

Implementing Face ID and Touch ID in SwiftUI

To implement Face ID and Touch ID in SwiftUI, we use the LocalAuthentication framework provided by Apple. The basic steps are:

  1. Import the LocalAuthentication framework.
  2. Create an LAContext instance to handle authentication.
  3. Check if biometric authentication is available on the device.
  4. Initiate the authentication process and handle the result.

Step 1: Import LocalAuthentication

First, import the LocalAuthentication framework in your SwiftUI file.

import LocalAuthentication

Step 2: Create the Authentication Function

Create a function to handle the authentication process. This function checks if biometric authentication is available and then initiates the authentication request.

import SwiftUI
import LocalAuthentication

class AuthenticationManager: ObservableObject {
    @Published var isLoggedIn = false
    @Published var authenticationError: String? = nil

    func authenticate() {
        let context = LAContext()
        var error: NSError?

        // Check if biometric authentication is available
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            let reason = "Authenticate to access your account."

            // Request biometric authentication
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
                DispatchQueue.main.async {
                    if success {
                        // Authentication successful
                        self.isLoggedIn = true
                        self.authenticationError = nil
                    } else {
                        // Authentication failed
                        self.isLoggedIn = false
                        self.authenticationError = authenticationError?.localizedDescription ?? "Authentication failed."
                    }
                }
            }
        } else {
            // Biometric authentication not available
            self.isLoggedIn = false
            self.authenticationError = error?.localizedDescription ?? "Biometrics not available."
        }
    }
}

Explanation:

  • An AuthenticationManager class is created to handle authentication logic and state management.
  • @Published var isLoggedIn tracks whether the user is logged in.
  • @Published var authenticationError stores any authentication errors for display.
  • The authenticate function initializes an LAContext.
  • context.canEvaluatePolicy checks if biometric authentication is available.
  • context.evaluatePolicy triggers the authentication process with a localized reason.
  • The completion handler processes the authentication result on the main thread, updating isLoggedIn and authenticationError accordingly.

Step 3: Integrating with SwiftUI View

Integrate the authentication process with a SwiftUI view.

struct ContentView: View {
    @ObservedObject var authenticationManager = AuthenticationManager()

    var body: some View {
        if authenticationManager.isLoggedIn {
            // Content to show when user is authenticated
            Text("Welcome! You are authenticated.")
        } else {
            VStack {
                Button("Authenticate") {
                    authenticationManager.authenticate()
                }
                if let error = authenticationManager.authenticationError {
                    Text("Error: (error)")
                        .foregroundColor(.red)
                }
            }
            .onAppear {
                // Attempt authentication when view appears
                authenticationManager.authenticate()
            }
        }
    }
}

Explanation:

  • An AuthenticationManager instance is observed by the view.
  • The view displays different content based on the isLoggedIn state.
  • An “Authenticate” button triggers the authentication process.
  • Any authentication error is displayed using a Text view.
  • Authentication is attempted automatically when the view appears using .onAppear.

Complete Example

import SwiftUI
import LocalAuthentication

class AuthenticationManager: ObservableObject {
    @Published var isLoggedIn = false
    @Published var authenticationError: String? = nil

    func authenticate() {
        let context = LAContext()
        var error: NSError?

        // Check if biometric authentication is available
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            let reason = "Authenticate to access your account."

            // Request biometric authentication
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
                DispatchQueue.main.async {
                    if success {
                        // Authentication successful
                        self.isLoggedIn = true
                        self.authenticationError = nil
                    } else {
                        // Authentication failed
                        self.isLoggedIn = false
                        self.authenticationError = authenticationError?.localizedDescription ?? "Authentication failed."
                    }
                }
            }
        } else {
            // Biometric authentication not available
            self.isLoggedIn = false
            self.authenticationError = error?.localizedDescription ?? "Biometrics not available."
        }
    }
}

struct ContentView: View {
    @ObservedObject var authenticationManager = AuthenticationManager()

    var body: some View {
        if authenticationManager.isLoggedIn {
            // Content to show when user is authenticated
            Text("Welcome! You are authenticated.")
        } else {
            VStack {
                Button("Authenticate") {
                    authenticationManager.authenticate()
                }
                if let error = authenticationManager.authenticationError {
                    Text("Error: (error)")
                        .foregroundColor(.red)
                }
            }
            .onAppear {
                // Attempt authentication when view appears
                authenticationManager.authenticate()
            }
        }
    }
}

@main
struct BiometricAuthApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Important Considerations

  • Error Handling: Properly handle errors to provide informative messages to the user.
  • Accessibility: Consider users who cannot use biometric authentication and provide alternative authentication methods.
  • Privacy: Inform users clearly about how their biometric data is used and stored.
  • Simulator Testing: Biometric authentication can be simulated in Xcode. Enable it from the simulator’s Hardware menu by selecting Face ID > Enrolled.

Conclusion

Implementing Face ID and Touch ID authentication in SwiftUI enhances the security and user experience of your applications. By using the LocalAuthentication framework, you can quickly integrate biometric authentication with robust error handling and UI feedback. Remember to consider accessibility and privacy to provide a comprehensive and user-friendly authentication experience.