数据持久化 #

一、持久化概述 #

Swift提供了多种数据持久化方案,适用于不同的场景。

1.1 持久化方案 #

方案 适用场景 特点
UserDefaults 简单配置 轻量、快速
FileManager 文件存储 灵活、通用
CoreData 复杂数据 强大、关系型
Keychain 敏感数据 安全、加密

二、UserDefaults #

2.1 基本用法 #

swift
let defaults = UserDefaults.standard

defaults.set("张三", forKey: "userName")
defaults.set(25, forKey: "userAge")
defaults.set(true, forKey: "isLoggedIn")

let userName = defaults.string(forKey: "userName") ?? ""
let userAge = defaults.integer(forKey: "userAge")
let isLoggedIn = defaults.bool(forKey: "isLoggedIn")

print(userName)
print(userAge)
print(isLoggedIn)

2.2 存储对象 #

swift
struct User: Codable {
    let id: Int
    let name: String
}

extension UserDefaults {
    func set<T: Encodable>(_ object: T, forKey key: String) {
        if let data = try? JSONEncoder().encode(object) {
            set(data, forKey: key)
        }
    }
    
    func get<T: Decodable>(_ type: T.Type, forKey key: String) -> T? {
        if let data = data(forKey: key) {
            return try? JSONDecoder().decode(T.self, from: data)
        }
        return nil
    }
}

let user = User(id: 1, name: "张三")
UserDefaults.standard.set(user, forKey: "currentUser")

if let savedUser = UserDefaults.standard.get(User.self, forKey: "currentUser") {
    print(savedUser.name)
}

2.3 实际应用 #

swift
class Settings: ObservableObject {
    @Published var isDarkMode: Bool {
        didSet {
            UserDefaults.standard.set(isDarkMode, forKey: "isDarkMode")
        }
    }
    
    @Published var fontSize: Int {
        didSet {
            UserDefaults.standard.set(fontSize, forKey: "fontSize")
        }
    }
    
    init() {
        self.isDarkMode = UserDefaults.standard.bool(forKey: "isDarkMode")
        self.fontSize = UserDefaults.standard.integer(forKey: "fontSize")
    }
}

三、FileManager #

3.1 文件路径 #

swift
let fileManager = FileManager.default

let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("data.json")

print("文档目录: \(documentsURL.path)")
print("文件路径: \(fileURL.path)")

3.2 写入文件 #

swift
let text = "Hello, World!"

do {
    try text.write(to: fileURL, atomically: true, encoding: .utf8)
    print("写入成功")
} catch {
    print("写入失败: \(error)")
}

3.3 读取文件 #

swift
do {
    let content = try String(contentsOf: fileURL, encoding: .utf8)
    print(content)
} catch {
    print("读取失败: \(error)")
}

3.4 删除文件 #

swift
do {
    try fileManager.removeItem(at: fileURL)
    print("删除成功")
} catch {
    print("删除失败: \(error)")
}

3.5 存储对象 #

swift
struct User: Codable {
    let id: Int
    let name: String
}

func saveUser(_ user: User, to url: URL) throws {
    let data = try JSONEncoder().encode(user)
    try data.write(to: url)
}

func loadUser(from url: URL) throws -> User {
    let data = try Data(contentsOf: url)
    return try JSONDecoder().decode(User.self, from: data)
}

let user = User(id: 1, name: "张三")
let userURL = documentsURL.appendingPathComponent("user.json")

try? saveUser(user, to: userURL)

if let loadedUser = try? loadUser(from: userURL) {
    print(loadedUser.name)
}

四、CoreData #

4.1 创建模型 #

swift
import CoreData

class CoreDataStack {
    static let shared = CoreDataStack()
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores { _, error in
            if let error = error {
                fatalError("CoreData错误: \(error)")
            }
        }
        return container
    }()
    
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    func save() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("保存失败: \(error)")
            }
        }
    }
}

4.2 创建实体 #

swift
extension UserEntity {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<UserEntity> {
        return NSFetchRequest<UserEntity>(entityName: "UserEntity")
    }
    
    @NSManaged public var id: Int64
    @NSManaged public var name: String?
    @NSManaged public var email: String?
}

func createUser(id: Int, name: String, email: String) {
    let context = CoreDataStack.shared.context
    
    let user = UserEntity(context: context)
    user.id = Int64(id)
    user.name = name
    user.email = email
    
    CoreDataStack.shared.save()
}

4.3 查询数据 #

swift
func fetchUsers() -> [UserEntity] {
    let context = CoreDataStack.shared.context
    let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest()
    
    do {
        return try context.fetch(request)
    } catch {
        print("查询失败: \(error)")
        return []
    }
}

func fetchUser(byId id: Int) -> UserEntity? {
    let context = CoreDataStack.shared.context
    let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest()
    request.predicate = NSPredicate(format: "id == %d", id)
    
    do {
        return try context.fetch(request).first
    } catch {
        print("查询失败: \(error)")
        return nil
    }
}

4.4 更新和删除 #

swift
func updateUser(_ user: UserEntity, name: String) {
    user.name = name
    CoreDataStack.shared.save()
}

func deleteUser(_ user: UserEntity) {
    let context = CoreDataStack.shared.context
    context.delete(user)
    CoreDataStack.shared.save()
}

五、Keychain #

5.1 Keychain封装 #

swift
import Security

class KeychainManager {
    static let shared = KeychainManager()
    
    func save(_ value: String, forKey key: String) -> Bool {
        guard let data = value.data(using: .utf8) else { return false }
        
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: data
        ]
        
        SecItemDelete(query as CFDictionary)
        
        let status = SecItemAdd(query as CFDictionary, nil)
        return status == errSecSuccess
    }
    
    func get(forKey key: String) -> String? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        
        guard status == errSecSuccess,
              let data = result as? Data,
              let value = String(data: data, encoding: .utf8) else {
            return nil
        }
        
        return value
    }
    
    func delete(forKey key: String) -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key
        ]
        
        let status = SecItemDelete(query as CFDictionary)
        return status == errSecSuccess
    }
}

5.2 使用示例 #

swift
KeychainManager.shared.save("my_api_key", forKey: "apiKey")

if let apiKey = KeychainManager.shared.get(forKey: "apiKey") {
    print(apiKey)
}

KeychainManager.shared.delete(forKey: "apiKey")

六、实际应用 #

6.1 缓存管理 #

swift
class CacheManager {
    static let shared = CacheManager()
    
    private let fileManager = FileManager.default
    private let cacheURL: URL
    
    init() {
        cacheURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
    }
    
    func save<T: Encodable>(_ object: T, forKey key: String) throws {
        let url = cacheURL.appendingPathComponent(key)
        let data = try JSONEncoder().encode(object)
        try data.write(to: url)
    }
    
    func load<T: Decodable>(_ type: T.Type, forKey key: String) throws -> T {
        let url = cacheURL.appendingPathComponent(key)
        let data = try Data(contentsOf: url)
        return try JSONDecoder().decode(T.self, from: data)
    }
    
    func clear() throws {
        let contents = try fileManager.contentsOfDirectory(at: cacheURL, includingPropertiesForKeys: nil)
        for url in contents {
            try fileManager.removeItem(at: url)
        }
    }
}

七、总结 #

本章学习了Swift数据持久化:

  • UserDefaults:简单配置存储
  • FileManager:文件存储
  • CoreData:复杂数据管理
  • Keychain:安全数据存储

最佳实践:

  • 根据场景选择方案
  • 敏感数据使用Keychain
  • 大量数据使用CoreData
  • 合理使用缓存

恭喜你完成了Swift完全指南的学习!

最后更新:2026-03-26