1

First and foremost I am very new to swift, and it is not my primary language. I have some difficulty to understand the concurrent constructs, and some are not yet available (until macOs 12 is released).

So I'd like the command line app to use the touchId of the laptop to access some data. I found numerous examples of the LAContext.evaluatePolicy call. However they seem all related to GUI apps (either in macOs or on iOS).

My problem is that I am working on a terminal app and the evaluatePolicy is evaluate on a private queue while the main thread finishes without leaving the time to actually show the authentication dialog.

I've tried to create a very simple reproducer, purely in swift. (But my terminal app has some code in C that calls the swift method).

import LocalAuthentication

@_cdecl("authenticate")
public func authenticateUser() {
    let context = LAContext()

    context.localizedFallbackTitle = "Please use your password"
    context.localizedCancelTitle = "Abort"

    var authorizationError: NSError?
    let permissions = context.canEvaluatePolicy(
            LAPolicy.deviceOwnerAuthentication, // TouchId or passcode
            error: &authorizationError
    )

    if permissions {
        let biometry = context.biometryType
        if (biometry != LABiometryType.touchID) {
            print("TouchID not available")
            return
        }

        let reason = "Identify yourself!"
        print(reason)

        context.evaluatePolicy(
                LAPolicy.deviceOwnerAuthentication,
                localizedReason: reason
        ) { (success: Bool, error: Error?) -> Void in
                if success {
                    print(" You may enter")
                } else {
                    print(" Authentication failed")
                    print(error?.localizedDescription ?? "Failed to authenticate")
                }
        }

        // <-------- block here
    } else {
        let ac = "Touch ID not available, Or Your device is not configured for Touch ID."
        print(ac)
    }
}

authenticateUser()

In this code snippet, I'd like the code to block to wait on the evaluatePolicy result. Via its reply completion handler or else.

I have tried to use DispatchQueue.main.async but this doesn't work, so I ultimately didn't do it correctly. Also I have tried using await/async, but swiftc tells me those aren't available on my system (concurrency is only available in macOS 12.0.0 or newer).

This can be compiled and executed this way:

$ swiftc main.swift
$ ./main
Identify yourself!

Thanks in advance for pointers to make this work.


Some versions

$ swiftc --version
Apple Swift version 5.5 (swiftlang-1300.0.31.1 clang-1300.0.29.1)
Target: x86_64-apple-darwin20.6.0
$ sw_vers
ProductName:    macOS
ProductVersion: 11.6
BuildVersion:   20G165
bric3
  • 40,072
  • 9
  • 91
  • 111
  • The only thing I notice is that you don't set 'Return' when the permissions guy is false. – El Tomato Oct 01 '21 at 07:23
  • If `permissions` is `false` it'll fall in the last `else` block, then the `authenticateUser` function exit normally. Adding an explicit `return` won't change the fact that `evaluatePolicy` is executing the `reply` callback on another thread. – bric3 Oct 01 '21 at 10:38
  • I agree, and I don't see anything wrong. – El Tomato Oct 01 '21 at 10:40
  • I think I'm missing a construct to make the main tread wait for a value, but I am unfamiliar with swift on this topic. – bric3 Oct 01 '21 at 10:49
  • If I were you, I would quickly convert it into an iOS project and test-run it with an actual device to see if it works. I have an iOS app that has nearly the same code as yours. – El Tomato Oct 02 '21 at 02:29
  • @ElTomato This code works if I use a `while status {}` loop to wait for the `status` to be `false`, which is done by the callback. But that is really ugly. – bric3 Oct 04 '21 at 09:20
  • You don't have the 'while' guy in your code. – El Tomato Oct 04 '21 at 09:29
  • Yes I added it after in order to workaround the issue. – bric3 Oct 04 '21 at 11:24

0 Answers0