ios - tutorial - Guardar y cargar desde KeyChain | Swift
keychain swift 4 (1)
Esta pregunta ya tiene una respuesta aquí:
- Guarda y recupera valor a través de KeyChain 5 respuestas
Cómo almacenar simplemente una cadena en el llavero y cargarla cuando sea necesario. Hay varias soluciones de SO que se refieren principalmente a Git repo. Pero necesito la solución más pequeña y simple en la última versión de Swift. Ciertamente, no quiero agregar git framework para simplemente almacenar una contraseña en mi proyecto.
Hay una solución similar Guarda y recupera valor a través de KeyChain , que no funcionó para mí. Cansado de los errores del compilador.
Fuente mas 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 ELIMINAR CONTRASEÑA
import Cocoa
import Security
// see https://.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], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])
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], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue])
// 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
}
}
Debe imaginar lo siguiente conectado a un campo de entrada de texto y una etiqueta, y luego tener cuatro botones conectados, uno para cada uno de los métodos.
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" }
}
}