Что такое KeychainAccess и зачем она нужна?
KeychainAccess — это легковесная, но мощная библиотека для Swift, которая предоставляет удобный и типобезопасный интерфейс для работы с Keychain Services (Связка ключей) от Apple. Keychain — это защищенное хранилище, встроенное в iOS, macOS, watchOS и tvOS, предназначенное для хранения конфиденциальных данных: паролей, ключей шифрования, токенов аутентификации, номеров кредитных карт и любой другой секретной информации.
Без библиотек работа с Keychain через нативный C-интерфейс (функции SecItemAdd, SecItemCopyMatching, SecItemUpdate, SecItemDelete) крайне громоздка, требует написания большого количества шаблонного кода и подвержена ошибкам. KeychainAccess решает эту проблему, предлагая:
- Простой Swift-синтаксис: работа через свойства (get/set) и методы с понятными названиями.
- Типобезопасность: данные автоматически преобразуются в
String,Dataили кастомные типы, реализующиеCodable. - Гибкость: поддержка различных уровней доступа (доступность приложения, после разблокировки устройства, с биометрией), работа с группами Keychain и iCloud синхронизацией.
- Безопасность по умолчанию: библиотека использует лучшие практики безопасности, установленные Apple.
Библиотека идеально подходит для хранения токенов OAuth, паролей пользователей, ключей API, seed-фраз криптокошельков и любых других данных, которые нельзя хранить в UserDefaults или в файловой системе в открытом виде.
Установка KeychainAccess
KeychainAccess не устанавливается через pip (Python) или npm (JavaScript). Это нативная библиотека для экосистемы Apple. Основные способы установки:
1. Swift Package Manager (SPM) — Рекомендуемый способ
В Xcode: File → Add Packages... → вставьте URL репозитория:
https://github.com/kishikawakatsumi/KeychainAccess.git
Выберите версию (обычно "Up to Next Major" с последней стабильной) и добавьте пакет в ваш target.
Или добавьте зависимость в Package.swift вашего пакета:
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2")
],
targets: [
.target(
name: "MyPackage",
dependencies: ["KeychainAccess"]),
]
)
2. CocoaPods
Добавьте строку в ваш Podfile:
pod 'KeychainAccess'
Затем выполните pod install.
3. Carthage
Добавьте в Cartfile:
github "kishikawakatsumi/KeychainAccess"
Затем выполните carthage update и добавьте фреймворк в ваш проект.
Быстрый старт с KeychainAccess
После установки импортируйте библиотеку: import KeychainAccess
Самый простой пример — сохранение и чтение строки:
import KeychainAccess
// Создаем экземпляр для работы с Keychain приложения
let keychain = Keychain(service: "com.example.myapp")
// Сохраняем значение
try keychain.set("my_secret_password", key: "user_password")
// Читаем значение
if let password = try keychain.get("user_password") {
print("Найденный пароль: \(password)")
} else {
print("Пароль не найден")
}
// Удаляем значение
try keychain.remove("user_password")
Этот код делает три основные операции: запись, чтение и удаление. Обратите внимание на обработку ошибок через try — операции с Keychain могут выбрасывать исключения (например, при проблемах с доступом или при отсутствии прав).
Основные методы и функции KeychainAccess
Рассмотрим 10 ключевых методов библиотеки. Все примеры предполагают наличие импорта import KeychainAccess.
1. init(service:accessGroup:) — Создание экземпляра
Что делает: Инициализирует объект Keychain для работы с определенным сервисом (обычно идентификатор вашего приложения или его bundle identifier). Позволяет указать группу доступа для шаринга данных между приложениями.
Параметры:
service: String— Имя сервиса. Рекомендуется использовать bundle identifier или уникальную строку.accessGroup: String?— (Опционально) Имя группы Keychain для шаринга данных между приложениями одного разработчика.
Пример:
let keychain = Keychain(service: "com.mycompany.myapp")
let sharedKeychain = Keychain(service: "com.mycompany.shared", accessGroup: "ABCDE12345.com.mycompany.shared")
2. set(_:key:) — Сохранение строки
Что делает: Сохраняет строковое значение в Keychain по указанному ключу. Если запись с таким ключом уже существует, она будет перезаписана.
Параметры:
value: String?— Значение для сохранения. Если передатьnil, метод поведет себя какremove.key: String— Уникальный ключ для доступа к значению.
Пример:
let keychain = Keychain(service: "com.example.app")
try keychain.set("user@example.com", key: "email")
try keychain.set(nil, key: "email") // Удалит запись
3. get(_:) — Чтение строки
Что делает: Извлекает строковое значение по указанному ключу. Возвращает String? — nil, если запись не найдена.
Параметры:
key: String— Ключ для поиска.
Пример:
let keychain = Keychain(service: "com.example.app")
if let token = try keychain.get("auth_token") {
print("Токен: \(token)")
} else {
print("Токен не найден")
}
4. remove(_:) — Удаление записи
Что делает: Удаляет запись из Keychain по указанному ключу. Если запись не существует, метод не выбрасывает ошибку (является идемпотентным).
Параметры:
key: String— Ключ записи для удаления.
Пример:
let keychain = Keychain(service: "com.example.app")
try keychain.remove("temporary_session")
try keychain.remove("non_existent_key") // Безопасно, ошибки не будет
5. set(_:key:) — Сохранение Data (перегрузка)
Что делает: Сохраняет бинарные данные (Data) в Keychain. Полезно для хранения сериализованных объектов, изображений или ключей шифрования.
Параметры:
value: Data?— Данные для сохранения.key: String— Ключ.
Пример:
let keychain = Keychain(service: "com.example.app")
let keyData = "my-secret-key".data(using: .utf8)!
try keychain.set(keyData, key: "encryption_key")
if let retrievedData = try keychain.getData("encryption_key") {
print("Получено \(retrievedData.count) байт")
}
6. getData(_:) — Чтение Data
Что делает: Извлекает бинарные данные по ключу. Возвращает Data?.
Параметры:
key: String— Ключ.
Пример:
let keychain = Keychain(service: "com.example.app")
if let data = try keychain.getData("user_profile_image") {
let image = UIImage(data: data)
}
7. allKeys() — Получение всех ключей
Что делает: Возвращает массив всех ключей ([String]), хранящихся в текущем сервисе Keychain. Полезно для инвентаризации или очистки.
Параметры: Нет.
Пример:
let keychain = Keychain(service: "com.example.app")
let keys = try keychain.allKeys()
print("Всего ключей: \(keys.count)")
for key in keys {
print("Найден ключ: \(key)")
}
8. contains(_:) — Проверка существования ключа
Что делает: Проверяет, существует ли запись с указанным ключом в Keychain. Возвращает Bool.
Параметры:
key: String— Ключ для проверки.
Пример:
let keychain = Keychain(service: "com.example.app")
if try keychain.contains("api_key") {
print("API ключ уже сохранен")
} else {
print("API ключ отсутствует, требуется авторизация")
}
9. accessibility(_:) — Настройка уровня доступа
Что делает: Устанавливает политику доступности для последующих операций записи. Определяет, когда данные могут быть прочитаны (например, только когда устройство разблокировано, или после первой разблокировки).
Параметры:
value: Accessibility— Одно из значений перечисления:.whenUnlocked,.afterFirstUnlock,.whenPasscodeSetThisDeviceOnlyи другие.
Пример:
let keychain = Keychain(service: "com.example.app")
.accessibility(.whenUnlockedThisDeviceOnly) // Данные доступны только на этом устройстве и только когда оно разблокировано
try keychain.set("sensitive_data", key: "private_key")
10. authenticationPolicy(_:) — Настройка биометрической аутентификации
Что делает: Устанавливает политику аутентификации (Touch ID, Face ID или код-пароль) для доступа к данным. При чтении таких данных система запросит биометрию у пользователя.
Параметры:
policy: AuthenticationPolicy— Политика:.biometryAny(любая биометрия),.biometryCurrentSet(только текущие отпечатки/лицо),.userPresence(биометрия или код-пароль).
Пример:
let keychain = Keychain(service: "com.example.app")
.accessibility(.whenUnlockedThisDeviceOnly)
.authenticationPolicy(.biometryCurrentSet)
// Сохраняем данные, защищенные биометрией
try keychain.set("my_medical_record", key: "health_data")
// Чтение запросит Face ID / Touch ID
if let data = try keychain.get("health_data") {
print("Данные получены после аутентификации: \(data)")
}