Checking if device Passcode is enabled using Swift

If you spent any time building apps for the Enterprise or business space odds are you have turn into the requirement to check if the device has a passcode enabled.

Mobile Device Management (MDM) Solutions are full of tools that handle this for you.  More often I’m finding that organizations are opting for lighter weight solutions to simplify their deployments.

The approach of checking for a passcode would easily become an anti-pattern in that the user at anytime can disable this feature.  I would recommend keeping this in mind and implement the check as only one part of a larger more comprehensive security strategy.

I would call the approach of checking for a passcode somewhat of an anti-pattern in that the user could disable this feature at anytime. If you implement this approach I would highly recommend that this is only one part of your security strategy.

Starting in iOS 8 you could finally check, without jailbreaking, if the device had a passcode set. All you have to do is create a new Keychain entry using the new kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly access option.

Below is an example of using the Keychain to test if the device passcode is enabled.

@available(iOS 8.0, *)
public func devicePasscodeEnabledUsingKeychain() -> Bool {
let query: [String:Any] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : UUID().uuidString,
kSecAttrAccessible as String: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
kSecValueData as String: "HelloWorld".data(using: String.Encoding.utf8)!,
kSecReturnAttributes as String : kCFBooleanTrue
]
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecItemNotFound {
let createStatus = SecItemAdd(query as CFDictionary, nil)
guard createStatus == errSecSuccess else { return false }
status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
}
guard status == errSecSuccess else { return false }
return true
}

Then in iOS 9 Apple introduced a much better way to perform this check. In the same way you check if Touch ID is enabled you can check if the device has a passcode.

Below is an example on how you can do this using canEvaluatePolicy.

public func devicePasscodeEnabled() -> Bool {
return LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
}

The below PasscodeUtils.swift combines the two approaches to allow for you to support this functionality back to iOS 8.

//
// PasscodeUtils.swift
//
// Created by Ben Bahrenburg on 12/31/16.
// Copyright © 2016 bencoding.com. All rights reserved.
//
import Foundation
import LocalAuthentication
public final class PasscodeUtils {
fileprivate var context = LAContext()
@available(iOS 8.0, *)
private func devicePasscodeEnabledUsingKeychain() -> Bool {
let query: [String:Any] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : UUID().uuidString,
kSecAttrAccessible as String: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
kSecValueData as String: "HelloWorld".data(using: String.Encoding.utf8)!,
kSecReturnAttributes as String : kCFBooleanTrue
]
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecItemNotFound {
let createStatus = SecItemAdd(query as CFDictionary, nil)
guard createStatus == errSecSuccess else { return false }
status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
}
guard status == errSecSuccess else { return false }
return true
}
public func devicePasscodeEnabled() -> Bool {
if #available(iOS 9.0, *) {
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
} else {
return devicePasscodeEnabledUsingKeychain()
}
}
@available(iOS 8.0, *)
public func deviceBiometricsEnabled() -> Bool {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
}