How to programmatically check support of 'Face Id' and 'Touch Id'

With Xcode 9, Look at LocalAuthentication -> LAContext -> LABiometryType.

LABiometryType is a enum with values as in attached image

enter image description here

You can check which authentication type supported by device between Touch ID and FaceID or none.

Edit:

Apple have updated values for this enum LABiometryType. none is deprecated now.

enter image description here

Extension to check supported Biometric Type with Swift 5:

import LocalAuthentication

extension LAContext {
    enum BiometricType: String {
        case none
        case touchID
        case faceID
    }

    var biometricType: BiometricType {
        var error: NSError?

        guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            @unknown default:
                #warning("Handle new Biometric type") 
            }
        }
        
        return  self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
    }
}

I've been struggling to get this to work and found that I needed to use a single instance of the LAContext and needed to call the LAContextInstance.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) before getting the biometryType. Here is my final code with support for older iOS versions:

import LocalAuthentication

static func biometricType() -> BiometricType {
    let authContext = LAContext()
    if #available(iOS 11, *) {
        let _ = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
        switch(authContext.biometryType) {
        case .none:
            return .none
        case .touchID:
            return .touch
        case .faceID:
            return .face
        }
    } else {
        return authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touch : .none
    }
}

enum BiometricType {
    case none
    case touch
    case face
}

As I am a big fan of extension. I phrase this answer a little differently. Essense is the same. This is a drop-in extension.

import LocalAuthentication

extension LAContext {
    enum BiometricType: String {
        case none
        case touchID
        case faceID
    }

    var biometricType: BiometricType {
        var error: NSError?

        guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            // Capture these recoverable error thru Crashlytics
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            }
        } else {
            return  self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
        }
    }
}

Use like this:

var currentType = LAContext().biometricType