0

I'm trying to lock my app when the app goes to background and the user can unlock the app when the app is in the foreground using biometric (FaceID/TouchID) authentication.

Here's a simple, reproducible example of my code:

import LocalAuthentication
import SwiftUI

struct ContentView: View {
    @State var isUnlocked = false
    
    var body: some View {
        if isUnlocked { 

            // if the app is unlocked, app shows the text "Unlocked"
            Text("Unlocked")
                .font(.title)

                // app gets locked when the app goes to background
                .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
                    self.isUnlocked = false
                    print("locked the app")
                }

        } else { 

            // if the app is locked, app shows the text "Locked" and app should show prompt FaceID/TouchID prompt
            Text("LOCKED")
                .font(.title)

                // FaceID prompt shows when the app opens (from Non-running state)
                .onAppear(perform: authenticate)

                // FaceID prompt shows when the app coming from background to foreground
                .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
                    authenticate()
                    print("back inside the app")
                }
        }
        
    }
    
    // I use this to prompt the biometric authentication
    func authenticate() {
        let context = LAContext()
        var error: NSError?
        
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            let reason = "We need to unlock your data"
            
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
                DispatchQueue.main.async {
                    if success {
                        // app gets unlocked if biometrix authentication succeeded
                        self.isUnlocked = true
                    } else {
                        // authentication failed
                    }
                }
            }
        } else {
            // no biometrics
        }
    }

}

When the user opens the app (from non-running state), FaceID prompt shows instantly without any delay. BUT when the app comes from background to foreground, FaceID prompt takes around 5 seconds to show up. I use the same function authenticate() to prompt the biometric authentication and Xcode console prints "back inside the app" instantly when the app comes from background to foreground.
So what causes that FaceID delay?

Please help me. Thanks.

umayanga
  • 2,254
  • 3
  • 15
  • 26
  • 1
    guessing, the async dispatch to set isUnlocked=true is called late for some reason. Maybe the SceneDelegate has to finish other business before this swiftUI part kicks in. For such an important feature i would observe and call from AppDelegate directly to set the isUnlocked state – Ol Sen Oct 21 '20 at 05:19
  • Thanks Ol Sen. I’ve observed the isUnlocked state from SceneDelegate and now the FaceID prompt shows up without any delay. – umayanga Oct 24 '20 at 14:49
  • yeah cool, chears, happy weekend. – Ol Sen Oct 24 '20 at 15:33

0 Answers0