Created
March 28, 2017 08:47
-
-
Save desmondmc/bde2f00cac34488722c330df75aca7dc to your computer and use it in GitHub Desktop.
Simple String Key Value Access to iOS Keychain
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Security | |
private let SecClass: String = kSecClass as String | |
private let SecAttrService: String = kSecAttrService as String | |
private let SecAttrAccessible: String = kSecAttrAccessible as String | |
private let SecAttrGeneric: String = kSecAttrGeneric as String | |
private let SecAttrAccount: String = kSecAttrAccount as String | |
private let SecMatchLimit: String = kSecMatchLimit as String | |
private let SecReturnData: String = kSecReturnData as String | |
private let SecValueData: String! = kSecValueData as String | |
private let KeyChainAccessibility = kSecAttrAccessibleWhenUnlocked | |
class Keychain { | |
@discardableResult static func save(key: String, value: String) -> Bool { | |
var query = basicKeychainQuery(with: key) | |
let encodedValue = value.data(using: .utf8)! | |
query[SecValueData] = encodedValue | |
let status: OSStatus = SecItemAdd(query as CFDictionary, nil) | |
if status == errSecSuccess { | |
return true | |
} else if status == errSecDuplicateItem { | |
return update(key: key, value: value) | |
} else { | |
return false | |
} | |
} | |
static func load(key: String) -> String? { | |
guard let data = keychainData(with: key) else { return nil } | |
return String(data: data, encoding: .utf8) as String? | |
} | |
@discardableResult static func delete(key: String) -> Bool { | |
let query = basicKeychainQuery(with: key) | |
let status = SecItemDelete(query as CFDictionary) | |
if status == errSecSuccess { | |
return true | |
} else { | |
return false | |
} | |
} | |
} | |
private func keychainData(with key: String) -> Data? { | |
var query = basicKeychainQuery(with: key) | |
query[SecMatchLimit] = kSecMatchLimitOne | |
query[SecReturnData] = kCFBooleanTrue | |
var result: AnyObject? | |
let status = withUnsafeMutablePointer(to: &result) { | |
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) | |
} | |
if status == noErr { | |
return result as? Data | |
} else { | |
return nil | |
} | |
} | |
private func update(key: String, value: String) -> Bool { | |
let query = basicKeychainQuery(with: key) | |
let encodedValue = value.data(using: .utf8)! | |
let update = [SecValueData: encodedValue] | |
let status = SecItemUpdate(query as CFDictionary, update as CFDictionary) | |
if status == errSecSuccess { | |
return true | |
} else { | |
return false | |
} | |
} | |
private func basicKeychainQuery(with key: String) -> [String:Any] { | |
let encodedKey = key.data(using: .utf8)! | |
return [SecClass: kSecClassGenericPassword, | |
SecAttrService: Bundle.main.bundleIdentifier!, | |
SecAttrAccessible: KeyChainAccessibility, | |
SecAttrGeneric: encodedKey, | |
SecAttrAccount: encodedKey] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment