UserDefaultsだとアプリを消すとデータもリセットされるが、Keychainを使うとアプリを消しても残り続けるので便利という感じ。
Contents 非表示
実装
KeychainHelper.swift
シングルトンのKeychain用のインスタンスを準備する、ここで保存、取得、削除を担わせるようにする。
import Security
import SwiftUI
final class KeychainHelper {
static let shared = KeychainHelper()
private init() {}
// 保存
func save(_ data: Data, forKey key: String) -> Bool {
// Keychainに保存するためのクエリ作成
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // データの種類を指定(ここでは一般的なパスワードとして保存)
kSecAttrAccount as String: key, // 保存するデータに関連するキーを指定
kSecValueData as String: data // 保存するデータ自体
]
// 既存のデータがある場合は削除する(同じキーで新しいデータを保存するため)
SecItemDelete(query as CFDictionary)
// Keychainにデータを保存 (成功すればtrueを返す)
return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
}
// 読み込み
func read(forKey key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key, // 読み込み対象のキーを指定
kSecReturnData as String: true, // データを返すように指定
kSecMatchLimit as String: kSecMatchLimitOne // 最初の一致するデータのみ取得
]
var dataTypeRef: AnyObject? // 取得したデータを格納するための変数
let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) // Keychainからデータを検索
if status == errSecSuccess {
return dataTypeRef as? Data // 取得したデータを返す
}
// データが見つからなかった場合はnilを返す
return nil
}
// 削除
func delete(forKey key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key
]
return SecItemDelete(query as CFDictionary) == errSecSuccess
}
}
Homeview.swift
import SwiftUI
struct HomeView: View {
@State private var userId: String = ""
@State private var password: String = ""
@State private var retrievedId: String = ""
@State private var retrievedPassword: String = ""
@State private var message: String = ""
private let idKey = "userID"
private let passwordKey = "userPassword"
var body: some View {
VStack(spacing: 8) {
Text("Keychain Example")
.font(.largeTitle)
.bold()
TextField("Enter User ID", text: $userId)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
SecureField("Enter Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button("Save to Keychain") {
saveToKeychain()
}
.buttonStyle(.borderedProminent)
Button("Retrieve from Keychain") {
retrieveFromKeychain()
}
.buttonStyle(.bordered)
Button("Delete from Keychain") {
deleteFromKeychain()
}
.buttonStyle(.bordered)
Text("Retrieved ID: \(retrievedId)")
Text("Retrieved Password: \(retrievedPassword)")
Text("Message: \(message)")
.foregroundColor(.red)
}
.padding()
}
}
private extension HomeView {
func saveToKeychain() {
guard let idData = userId.data(using: .utf8), let passwordData = password.data(using: .utf8) else {
message = "Failed to encode data"
return
}
let idSaved = KeychainHelper.shared.save(idData, forKey: idKey)
let passwordSaved = KeychainHelper.shared.save(passwordData, forKey: passwordKey)
message = (idSaved && passwordSaved) ? "Saved successfully" : "Failed to save"
}
func retrieveFromKeychain() {
if let idData = KeychainHelper.shared.read(forKey: idKey), let passwordData = KeychainHelper.shared.read(forKey: passwordKey) {
retrievedId = String(data: idData, encoding: .utf8) ?? "N/A"
retrievedPassword = String(data: passwordData, encoding: .utf8) ?? "N/A"
message = "Retrieved successfully"
} else {
message = "No data found"
}
}
func deleteFromKeychain() {
let idDeleted = KeychainHelper.shared.delete(forKey: idKey)
let passwordDeleted = KeychainHelper.shared.delete(forKey: passwordKey)
if idDeleted && passwordDeleted {
retrievedId = ""
retrievedPassword = ""
message = "Deleted successfully!"
} else {
message = "Failed to delete data"
}
}
}
Keychainのメリット
- セキュリティが高い
- Keychainはデータを暗号化して保存するため、セキュリティが高い。
- 保存されたデータはOSのセキュリティフレームワークに保護され、他のアプリやプロセスからアクセスできない。
- iCloud同期
- iCloudと連携して、同じApple IDを使用している他のデバイスともデータを同期することができる。
- システム統合
- Appleのセキュリティフレームワークと統合されており、ユーザーの認証情報(例:パスワードやセキュリティトークン)の管理が簡単になる。
- 自動でデータの暗号化と復号化
- 開発者はKeychainにデータを保存する際に暗号化の設定を行う必要がなく、Keychainが自動でデータを暗号化および復号化する。
- 簡単に利用できるAPI
- KeychainのAPIは使いやすく、データの保存、読み込み、削除を簡単に行うことができる。
Keychainのデメリット
- 容量制限
- Keychainには保存できるデータの容量に制限があり、大量のデータを保存することはできない。通常はパスワードや認証情報など、少量のデータ向け。
- データの同期には時間がかかる場合がある
- iCloud同期機能を使用している場合、デバイス間での同期に時間がかかることがある。
- 読み書きのパフォーマンスに影響
- Keychainは暗号化されたストレージを提供するため、大量のデータを頻繁に読み書きする場合、パフォーマンスが低下することがある。
- iOS/macOS専用
- KeychainはAppleのエコシステム内でのみ利用可能であり、Androidや他のプラットフォームでの利用ができない。クロスプラットフォームでデータを扱う必要がある場合は、他のストレージ方法を検討する必要がある。
- Keychainのクリア時の注意点
- Keychainからデータを削除する際に、すべてのデータが一度に削除される場合があり、重要な情報が消失するリスクがるため、削除操作には注意が必要。