7

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.

Community
  • 1
  • 1
Simon
  • 25,468
  • 44
  • 152
  • 266

2 Answers2

11

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.

Tumata
  • 1,507
  • 1
  • 11
  • 14
  • Good Answer, works on iOS10. Same thing in Swift3: `func deviceHasPasscodeEnabled()->Bool { let myContext = LAContext() var authError:NSError? = nil if (myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &authError)) { return true } else { return false } }` – JBA Oct 24 '16 at 12:49
4

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
}
Aaron Bratcher
  • 6,051
  • 2
  • 39
  • 70