We have an app that stores sensitive data. We've enabled file protection, but that only has an effect if the user has set a passcode. If the user hasn't set a passcode, we need to show an alert telling the user to do that, and then not to load the rest of the app.
Basically we're in the exact situation as in this question, and my question is exactly their question. But the accepted answer there is "enable file protection", which is not an answer to that question, or to this one; I'm already enabling file protection and it doesn't tell me whether they've set a passcode or not.
So is it possible to check, and if so, how? Ideally we'd like to check whether the user has set a long passcode or a simple one, and if they've only set a simple one we would warn them to set a proper one.
There is an official answer to this question with iOS 9:
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError])
{
// Device has either passcode enable (or passcode and touchID)
}
else
{
// Device does not have a passcode
// authError can be checked for more infos (is of type LAError)
}
A link to Apple's LAContext class
A note: LAPolicyDeviceOwnerAuthentication is a constant available in iOS 9 only. iOS 8 had LAPolicyDeviceOwnerAuthenticationWithBiometrics available. It can be used to achieve some results but these do not answer your question.
With iOS 8, there is now a way to check that the user has a passcode set. This code will crash on iOS 7.
Objective-C:
-(BOOL) deviceHasPasscode {
NSData* secret = [@"Device has passcode set?" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *attributes = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: @"LocalDeviceServices", (__bridge id)kSecAttrAccount: @"NoAccount", (__bridge id)kSecValueData: secret, (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly };
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
if (status == errSecSuccess) { // item added okay, passcode has been set
SecItemDelete((__bridge CFDictionaryRef)attributes);
return true;
}
return false;
}
Swift:
func deviceHasPasscode() -> Bool {
let secret = "Device has passcode set?".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let attributes = [kSecClass as String:kSecClassGenericPassword, kSecAttrService as String:"LocalDeviceServices", kSecAttrAccount as String:"NoAccount", kSecValueData as String:secret!, kSecAttrAccessible as String:kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly]
let status = SecItemAdd(attributes, nil)
if status == 0 {
SecItemDelete(attributes)
return true
}
return false
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With