Guardar y cargar desde KeyChain | Swift

Cómo save una cadena en Keychain y cargarla cuando sea necesario. Hay varias soluciones SO que se refieren principalmente a Git repo. Pero necesito la solución más simple y más simple en el último Swift. Ciertamente, no quiero agregar git framework para simplemente almacenar una contraseña en mi proyecto.

Hay una solución similar Guardar y recuperar valor a través de KeyChain , que no funcionó para mí. Cansado con errores de comstackdor.

Fuente más simple

import Foundation import Security // Constant Identifiers let userAccount = "AuthenticatedUser" let accessGroup = "SecuritySerivice" /** * User defined keys for new entry * Note: add new keys for new secure item and use them in load and save methods */ let passwordKey = "KeyForPassword" // Arguments for the keychain queries let kSecClassValue = NSString(format: kSecClass) let kSecAttrAccountValue = NSString(format: kSecAttrAccount) let kSecValueDataValue = NSString(format: kSecValueData) let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword) let kSecAttrServiceValue = NSString(format: kSecAttrService) let kSecMatchLimitValue = NSString(format: kSecMatchLimit) let kSecReturnDataValue = NSString(format: kSecReturnData) let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne) public class KeychainService: NSObject { /** * Exposed methods to perform save and load queries. */ public class func savePassword(token: NSString) { self.save(passwordKey, data: token) } public class func loadPassword() -> NSString? { return self.load(passwordKey) } /** * Internal methods for querying the keychain. */ private class func save(service: NSString, data: NSString) { let dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) // Delete any existing items SecItemDelete(keychainQuery as CFDictionaryRef) // Add the new keychain item SecItemAdd(keychainQuery as CFDictionaryRef, nil) } private class func load(service: NSString) -> NSString? { // Instantiate a new default keychain query // Tell the query to return a result // Limit our results to one item let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) var dataTypeRef :AnyObject? // Search for the keychain items let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var contentsOfKeychain: NSString? = nil if status == errSecSuccess { if let retrievedData = dataTypeRef as? NSData { contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding) } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return contentsOfKeychain } } 

Ejemplo de llamada

 KeychainService.savePassword("Pa55worD") let password = KeychainService.loadPassword() // password = "Pa55worD" 

SWIFT 4: VERSIÓN CON ACTUALIZACIÓN Y RETIRAR CONTRASEÑA

 import Cocoa import Security // see https://stackoverflow.com/a/37539998/1694526 // Arguments for the keychain queries let kSecClassValue = NSString(format: kSecClass) let kSecAttrAccountValue = NSString(format: kSecAttrAccount) let kSecValueDataValue = NSString(format: kSecValueData) let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword) let kSecAttrServiceValue = NSString(format: kSecAttrService) let kSecMatchLimitValue = NSString(format: kSecMatchLimit) let kSecReturnDataValue = NSString(format: kSecReturnData) let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne) public class KeychainService: NSObject { class func updatePassword(service: String, account:String, data: String) { if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary) if (status != errSecSuccess) { if let err = SecCopyErrorMessageString(status, nil) { print("Read failed: \(err)") } } } } class func removePassword(service: String, account:String) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) // Delete any existing items let status = SecItemDelete(keychainQuery as CFDictionary) if (status != errSecSuccess) { if let err = SecCopyErrorMessageString(status, nil) { print("Remove failed: \(err)") } } } class func savePassword(service: String, account:String, data: String) { if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) // Add the new keychain item let status = SecItemAdd(keychainQuery as CFDictionary, nil) if (status != errSecSuccess) { // Always check the status if let err = SecCopyErrorMessageString(status, nil) { print("Write failed: \(err)") } } } } class func loadPassword(service: String, account:String) -> String? { // Instantiate a new default keychain query // Tell the query to return a result // Limit our results to one item let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) var dataTypeRef :AnyObject? // Search for the keychain items let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var contentsOfKeychain: String? if status == errSecSuccess { if let retrievedData = dataTypeRef as? Data { contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8) } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return contentsOfKeychain } } 

Necesita imaginar el siguiente cable con un campo de input de text y una label, luego tener cuatro botones conectados, uno para cada uno de los methods.

 class ViewController: NSViewController { @IBOutlet weak var enterPassword: NSTextField! @IBOutlet weak var retrievedPassword: NSTextField! let service = "myService" let account = "myAccount" // will only work after @IBAction func updatePassword(_ sender: Any) { KeychainService.updatePassword(service: service, account: account, data: enterPassword.stringValue) } @IBAction func removePassword(_ sender: Any) { KeychainService.removePassword(service: service, account: account) } @IBAction func passwordSet(_ sender: Any) { let password = enterPassword.stringValue KeychainService.savePassword(service: service, account: account, data: password) } @IBAction func passwordGet(_ sender: Any) { if let str = KeychainService.loadPassword(service: service, account: account) { retrievedPassword.stringValue = str } else {retrievedPassword.stringValue = "Password does not exist" } } }