スマホに安全に保存したいもの例:
– ログイン時に受け取る アクセストークン
– ユーザーの パスワード
– API の Bearer Token
これらは UserDefaults に保存してはいけない
Keychain に保存する
### Modelsフォルダ
KeychainManager.swift
import Foundation
import Security
class KeychainManager {
static func save(key: String, value: String) {
let data = value.data(using: .utf8)!
// 既存があれば削除
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key
] as CFDictionary
SecItemDelete(query)
// 保存
let attributes = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: data
] as CFDictionary
SecItemAdd(attributes, nil)
}
static func load(key: String) -> String? {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecReturnData: true,
kSecMatchLimit: kSecMatchLimitOne
] as CFDictionary
var result: AnyObject?
SecItemCopyMatching(query, &result)
if let data = result as? Data {
return String(data: data, encoding: .utf8)
}
return nil
}
static func delete(key: String) {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key
] as CFDictionary
SecItemDelete(query)
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var tokenInput = ""
@State private var storedToken = ""
var body: some View {
VStack(spacing: 20) {
TextField("トークンを入力", text: $tokenInput)
.textFieldStyle(.roundedBorder)
.padding()
Button("保存") {
KeychainManager.save(key: "myToken", value: tokenInput)
}
.buttonStyle(.borderedProminent)
Button("読み込み") {
storedToken = KeychainManager.load(key: "myToken") ?? "(なし)"
}
Text("保存されているトークン:\(storedToken)")
.padding()
Button("削除") {
KeychainManager.delete(key: "myToken")
storedToken = "(削除されました)"
}
.foregroundColor(.red)
}
.padding()
}
}

ほう、なるほど