Almacenar secretos en Keychain
Guarde y recupere credenciales de forma segura.
Almacenar secretos en Keychain es una lección gratuita de Swift Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Swift Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Swift Academy incluye 4 lecciones en total.
Por qué usar Keychain
Las contraseñas, los tokens y las claves nunca deben almacenarse en UserDefaults ni en archivos sin formato, porque son fáciles de leer. Keychain es una base de datos cifrada y gestionada por el sistema operativo para secretos pequeños, protegida por el hardware y el código del usuario. Es el único lugar correcto para almacenar credenciales.
import Security
// Keychain stores secrets encrypted at rest,
// survives app updates, and gates access by policy.Los elementos son diccionarios
La API Keychain Services está basada en C: se describe un elemento con un diccionario de consulta [String: Any] que utiliza claves constantes kSec.... La misma estructura de diccionario se reutiliza para añadir, buscar, actualizar y eliminar.
import Security
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "user@example.com",
kSecAttrService as String: "com.example.app"
]
_ = queryClases de elementos
La clave kSecClass selecciona el tipo de elemento. kSecClassGenericPassword cubre tokens y secretos de la aplicación; kSecClassInternetPassword almacena credenciales del servidor con atributos de host y protocolo. La mayoría de los secretos de las aplicaciones usan una contraseña genérica.
import Security
// kSecClassGenericPassword -> tokens, API keys
// kSecClassInternetPassword -> server logins
// kSecClassKey / Certificate -> crypto material
let cls = kSecClassGenericPassword
_ = clsAdición de un elemento
SecItemAdd inserta un elemento nuevo. Incluya los datos bajo kSecValueData como Data, además de los atributos que lo identifican. Devuelve un OSStatus; errSecSuccess indica que la operación se realizó correctamente.
import Security
func save(_ token: String, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: Data(token.utf8)
]
return SecItemAdd(query as CFDictionary, nil)
== errSecSuccess
}Gestión de duplicados
Si añade un elemento cuyos atributos identificativos ya existen, se devuelve errSecDuplicateItem. Una operación de guardado robusta intenta usar SecItemAdd y, si encuentra un duplicado, recurre a SecItemUpdate: un patrón de actualización o inserción.
import Security
func upsert(_ data: Data, account: String) -> Bool {
let base: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
var add = base
add[kSecValueData as String] = data
let status = SecItemAdd(add as CFDictionary, nil)
if status == errSecDuplicateItem {
return SecItemUpdate(base as CFDictionary,
[kSecValueData as String: data] as CFDictionary)
== errSecSuccess
}
return status == errSecSuccess
}Volver a leer un elemento
SecItemCopyMatching realiza la búsqueda. Para obtener los bytes secretos, debe establecer kSecReturnData en true y kSecMatchLimit en kSecMatchLimitOne. El resultado se devuelve mediante un parámetro de salida como CFTypeRef.
import Security
func load(account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var result: CFTypeRef?
let status = SecItemCopyMatching(
query as CFDictionary, &result)
guard status == errSecSuccess else { return nil }
return result as? Data
}Actualizar un elemento
SecItemUpdate recibe dos diccionarios: una consulta que encuentra el elemento y un diccionario con los atributos que se actualizarán. Solo cambian los atributos que indique; todo lo demás se conserva.
import Security
func update(_ newData: Data, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let attrs: [String: Any] = [
kSecValueData as String: newData]
return SecItemUpdate(query as CFDictionary,
attrs as CFDictionary) == errSecSuccess
}Eliminar un elemento
SecItemDelete elimina los elementos coincidentes. Eliminar algo que no existe devuelve errSecItemNotFound, que puede tratar como un éxito al borrar las credenciales durante el cierre de sesión.
import Security
func delete(account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
|| status == errSecItemNotFound
}Interpretar OSStatus
Los errores son códigos enteros de OSStatus. SecCopyErrorMessageString convierte uno de ellos en una descripción comprensible, lo que resulta inestimable al depurar por qué ha fallado un guardado o una lectura.
import Security
func describe(_ status: OSStatus) -> String {
return SecCopyErrorMessageString(status, nil)
as String? ?? "OSStatus \(status)"
}Identificar elementos de forma única
Los elementos se comparan mediante su combinación de atributos —normalmente kSecAttrService y kSecAttrAccount para las contraseñas genéricas—. Elija una cadena de servicio estable y específica de la app para que los distintos secretos nunca entren en conflicto.
import Security
// Uniqueness for generic passwords usually comes from:
// service (your bundle id) + account (the username)
let service = "com.example.app.auth"
let account = "current-user"
_ = (service, account)Un pequeño wrapper
Como la API sin abstraer es verbosa, los equipos suelen envolverla en un tipo pequeño que expone save, read y delete. Así, la configuración de kSec queda en un solo lugar y los puntos de llamada se mantienen limpios.
import Security
struct TokenStore {
let service = "com.example.app.auth"
func read(_ account: String) -> Data? {
let q: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var out: CFTypeRef?
return SecItemCopyMatching(q as CFDictionary, &out)
== errSecSuccess ? out as? Data : nil
}
}Comprobación rápida
Recuerde cuál es la forma correcta de recuperar los bytes de un secreto almacenado.
Resumen
Ha aprendido las operaciones CRUD de Keychain:
- Guarde las credenciales en Keychain, que está cifrado, nunca en UserDefaults ni en archivos.
- Describa los elementos con diccionarios de consulta de
kSec; elija una clase comokSecClassGenericPassword. SecItemAdd/SecItemCopyMatching/SecItemUpdate/SecItemDeletecubren todo el ciclo de vida; gestioneerrSecDuplicateItemcon una operación upsert.- Identifique los elementos mediante el servicio y la cuenta, y decodifique
OSStatuspara depurar.
Preguntas frecuentes
¿La lección «Almacenar secretos en Keychain» es gratis?
Sí — el texto completo de «Almacenar secretos en Keychain» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Swift Academy, actualiza a CoddyKit PRO. El curso de Swift Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Almacenar secretos en Keychain»?
Guarde y recupere credenciales de forma segura. Practicas Swift Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Swift Academy?
No se requiere experiencia previa. Swift Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Almacenar secretos en Keychain»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Swift Academy?
Sí. Cada lección de Swift Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Almacenar secretos en Keychain
- Control de acceso a Keychain
- Autenticación biométrica
- Protección de datos y cifrado