数据持久化
Core Data、UserDefaults、Keychain。
35 分钟
数据持久化
iOS 本地存储分层:UserDefaults 存偏好、Keychain 存敏感凭证、Core Data / SwiftData 存结构化数据。对照 /roadmaps/ios「Data Persistence」节点。
学习目标
- 选择合适的存储方案(轻量 vs 关系型)
- 使用 UserDefaults 与 Keychain 读写配置与 Token
- 了解 Core Data 实体建模与 CRUD 基础
- 对照 Spring JPA 与浏览器 localStorage
核心概念
UserDefaults — 轻量键值
// 写入
UserDefaults.standard.set(true, forKey: "hasSeenOnboarding")
UserDefaults.standard.set("dark", forKey: "theme")
// 读取
let seen = UserDefaults.standard.bool(forKey: "hasSeenOnboarding")
let theme = UserDefaults.standard.string(forKey: "theme") ?? "light"
适合主题、引导页标记等小数据。不要存 Token 或密码。
Keychain — 安全存储
import Security
enum KeychainHelper {
static func save(_ data: Data, service: String, account: String) throws {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: data,
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandled(status) }
}
static func load(service: String, account: String) throws -> Data? {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecReturnData: true,
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess else { return nil }
return result as? Data
}
}
enum KeychainError: Error {
case unhandled(OSStatus)
}
Keychain 加密存储,适合 JWT、API Key;系统级安全,卸载 App 行为可配置。
Core Data 基础
Core Data 是 Apple 的 ORM 框架:Entity → NSManagedObject,支持关系、迁移、iCloud 同步。
// 简化的 CRUD 思路(需 Xcode Data Model 定义 LessonEntity)
func saveLesson(_ lesson: Lesson, context: NSManagedObjectContext) throws {
let entity = LessonEntity(context: context)
entity.id = Int64(lesson.id)
entity.title = lesson.title
entity.completed = lesson.completed
try context.save()
}
func fetchLessons(context: NSManagedObjectContext) throws -> [Lesson] {
let request = LessonEntity.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
let results = try context.fetch(request)
return results.map { Lesson(id: Int($0.id), title: $0.title ?? "", completed: $0.completed) }
}