问题
LAContext has method to check if device can evaluate touch ID and gives error message. Problem is that same error message "LAErrorPasscodeNotSet" is given by system in two cases:
1) If user has Touch ID, but turned it off in settings (iPhone 5s with iOS8)
2) If device doesn't have Touch ID (iPad with iOS8)
Q: How to check if device supports Touch ID, but haven't turned it on in settings?
Update:
Had created ticket to Apple regarding this bug (ID# 18364575) and received answer:
"Engineering has determined that this issue behaves as intended based on the following information:
If passcode is not set, you will not be able to detect Touch ID presence. Once the passcode is set, canEvaluatePolicy will eventually return LAErrorTouchIDNotAvailable or LAErrorTouchIdNotEnrolled and you will be able to detect Touch ID presence/state.
If users have disabled passcode on phone with Touch ID, they knew that they will not be able to use Touch ID, so the apps don't need to detect Touch ID presence or promote Touch ID based features. "
回答1:
Maybe you could write your own method to check which device are you running on, because if returned error is the same, it would be hard to figure out exactly if Touch ID is supported. I would go with something like this:
int sysctlbyname(const char *, void *, size_t *, void *, size_t);
- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
size_t size;
sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
char *answer = malloc(size);
sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
free(answer);
return results;
}
- (NSString *)modelIdentifier
{
return [self getSysInfoByName:"hw.machine"];
}
After having the model identifier, I would just check if model identifier equals is one of the models that support Touch ID:
- (BOOL)hasTouchID
{
NSArray *touchIDModels = @[ @"iPhone6,1", @"iPhone6,2", @"iPhone7,1", @"iPhone7,2", @"iPad5,3", @"iPad5,4", @"iPad4,7", @"iPad4,8", @"iPad4,9" ];
NSString *model = [self modelIdentifier];
return [touchIDModels containsObject:model];
}
The array contains all model ID's which support Touch ID, which are:
- iPhone 5s
- iPhone 6
- iPhone 6+
- iPad Air 2
- iPad Mini 3
The only downside of this method is that once new devices are released with Touch ID, the model array will have to be updated manually.
回答2:
In Swift 3
fileprivate func deviceSupportsTouchId(success: @escaping () -> (), failure: @escaping (NSError) -> ()) {
let context = LAContext()
var authError: NSError?
let touchIdSetOnDevice = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &authError)
if touchIdSetOnDevice {
DispatchQueue.main.async {
success()
}
}
else {
DispatchQueue.main.async {
failure(error!)
}
}
}
deviceSupportsTouchId() return Success if the device has touch Id capability.
If not then, function will return error, giving you the following error code if touchIDNotEnrolled is not set yet.
LAError.Code.touchIDNotEnrolled.rawValue
You can handle it using this value.
回答3:
Here you can check, Touch-ID and Face-ID both (with iOS 11+)
Use property biometryType
of LAContext
to check and evaluate available biometric policy. (For a passcode authentication , when biometric fails, use: LAPolicyDeviceOwnerAuthentication
)
Try this and see:
Objective-C:
LAContext *laContext = [[LAContext alloc] init];
NSError *error;
// For a passcode authentication , when biometric fails, use: LAPolicyDeviceOwnerAuthentication
//if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthentication error:&error]) {
if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
if (error != NULL) {
// handle error
} else {
if (@available(iOS 11, *)) {
if (laContext.biometryType == LABiometryTypeFaceID) {
//localizedReason = "Unlock using Face ID"
NSLog(@"FaceId support");
} else if (laContext.biometryType == LABiometryTypeTouchID) {
//localizedReason = "Unlock using Touch ID"
NSLog(@"TouchId support");
} else {
//localizedReason = "Unlock using Application Passcode"
NSLog(@"No biometric support or Denied biometric support");
}
} else {
// Fallback on earlier versions
}
[laContext evaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"Test Reason" reply:^(BOOL success, NSError * _Nullable error) {
if (error != NULL) {
// handle error
} else if (success) {
// handle success response
} else {
// handle false response
}
}];
}
}
Swift:
let laContext = LAContext()
var error: NSError?
let biometricsPolicy = LAPolicy.deviceOwnerAuthentication //LAPolicy.deviceOwnerAuthenticationWithBiometrics
if laContext.isCredentialSet(LACredentialType.applicationPassword) {
print("Passsword is set")
}
let localizedFallbackTitle = "Unlock Using Device Passcode"
let localizedCancelTitle = "Use Application Passcode"
if (laContext.canEvaluatePolicy(biometricsPolicy, error: &error)) {
if let laError = error {
print("laError - \(laError)")
return
}
//print("biometricsPolicy - \(biometricsPolicy.rawValue)")
UINavigationBar.appearance().tintColor = UIColor.red
var localizedReason = "My Reason to be displayed on face id prompt"
if #available(iOS 11.0, *) {
if (laContext.biometryType == LABiometryType.faceID) {
//localizedReason = "Unlock using Face ID"
print("FaceId support")
} else if (laContext.biometryType == LABiometryType.touchID) {
//localizedReason = "Unlock using Touch ID"
print("TouchId support")
} else {
//localizedReason = "Unlock using Application Passcode"
print("No Biometric support")
}
} else {
// Fallback on earlier versions
}
laContext.localizedFallbackTitle = localizedFallbackTitle
laContext.localizedCancelTitle = localizedCancelTitle
//laContext.localizedReason = "test loc reason"
laContext.evaluatePolicy(biometricsPolicy, localizedReason: localizedReason, reply: { (isSuccess, error) in
DispatchQueue.main.async(execute: {
if let laError = error {
print("laError - \(laError)")
} else {
if isSuccess {
print("sucess")
} else {
print("failure")
}
}
})
})
}
回答4:
Integrating Touch ID
Now we get to the main part of the tutorial… integrating Touch ID with the app. As it turns out, Apple has made some fairly standard code for accessing Touch ID. The code comes from the Local Authentication Framework and is as follows:
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = @"Used for quick and secure access to the test app";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
// User authenticated successfully, take appropriate action
}
else {
// User did not authenticate successfully, look at error and take appropriate action
}
}];
}
else {
// Could not evaluate policy; look at authError and present an appropriate message to user }
Lets take a look at each line to see what it does:
Line 1: Here we create an LAContext object. The LAContext class is responsible for handling the context for the authentication. Put simply, we use an LAContext object to check if a type of authentication is available. In the case of this tutorial, we will later be checking “if” touch ID is an option.
Line 2: We need an NSError so that the LAContext can use it to return if there is an error.
Line 3: We set an NSString with a description that it put on screen to let the user know why the touch ID view has appeared on screen.
Line 5: This is where we put the LAContext constant to use by calling the canEvaluatePolicy: method and sending it an LAPolicy constant as an argument. In this case, we pass LAPolicyDeviceOwnerAuthenticationWithBiometrics. If this fails, either touch ID is not configured on a compatible device, or touch ID is not available on the device… think an iPhone 4S, 5 or 5c running the app. Also, this doesn’t take in to account a device running iOS 7, so if you plan to run finger print authentication on an app, make sure you check that you are working with a compatible device and if not, make other options available such as password on pin code to access the app.
Lines 6, 7 and 8: If the user can authenticate with biometrics we can now call the evaluatePolicy method on our LAContext object. We do this by passing the same constant over, LAPolicyDeviceOwnerAuthenticationWithBiometrics, as well as passing our reason string and then specifying a block for the response to be handled.
We will get either a YES or a NO as a result. If a YES then line 10 is where we put code for a positive response. Likewise, line 12 is where we put our failure code.
Finally on line 15, we have the ELSE statement which runs if line 5 fails the test… i.e., biometrics is not available. We can check the authError pointer to get the reason and present it to the user if needed.
Finally, to get this to not show errors, we need to import the local authentication framework in to our project:
So, lets add this code to our project. Open ViewController.m and at the top, import the local authentication framework.
For more detail visit this link : http://www.devfright.com/touch-id-tutorial-objective-c/
回答5:
You can find out if a device supports Biometric scanning (touchID and faceID) by checking the error code as shown below:
func deviceSupportsBiometricScanning() -> Bool {
var authError: NSError?
let _ = LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError)
return authError?.code != kLAErrorBiometryNotAvailable.hashValue
}
来源:https://stackoverflow.com/questions/26026563/ios8-check-if-device-has-touch-id