案例:扩展 Todo 应用支持离线存储
目标
通过集成 Core Data 为现有的 Todo 应用添加离线存储功能,确保数据在应用关闭后仍能持久化,并支持跨设备同步(需结合 iCloud 或其他同步方案)。
实现步骤
1. 配置 Core Data 环境
1.1 创建 Core Data 模型文件
- 在 Xcode 中新建
Data Model文件(如TodoModel.xcdatamodeld)。 - 定义
TodoItem实体,包含以下属性:id: UUID(唯一标识)title: StringisCompleted: BoolcreatedAt: Date
1.2 生成 NSManagedObject 子类
@objc(TodoItem)
public class TodoItem: NSManagedObject, Identifiable {
@NSManaged public var id: UUID
@NSManaged public var title: String
@NSManaged public var isCompleted: Bool
@NSManaged public var createdAt: Date
}
2. 重构 Model 层
2.1 将内存模型迁移至 Core Data
- 移除原有的
Todo结构体,改用TodoItem实体。 - 创建
DataController类管理 Core Data 上下文:
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "TodoModel")
init() {
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load Core Data: \(error)")
}
}
}
}
3. 修改 ViewModel 层
3.1 适配 Core Data 操作
- 在
TodoViewModel中注入DataController:
class TodoViewModel: ObservableObject {
private let dataController: DataController
@Published var todos: [TodoItem] = []
init(dataController: DataController) {
self.dataController = dataController
fetchTodos()
}
func fetchTodos() {
let request: NSFetchRequest<TodoItem> = TodoItem.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
todos = (try? dataController.container.viewContext.fetch(request)) ?? []
}
func addTodo(title: String) {
let newTodo = TodoItem(context: dataController.container.viewContext)
newTodo.id = UUID()
newTodo.title = title
newTodo.isCompleted = false
newTodo.createdAt = Date()
saveContext()
}
private func saveContext() {
do {
try dataController.container.viewContext.save()
fetchTodos() // 刷新数据
} catch {
print("保存失败: \(error)")
}
}
}
4. 更新 View 层
4.1 绑定 Core Data 数据源
- 修改
TodoListView以响应@FetchRequest或 ViewModel 的@Published数据:
struct TodoListView: View {
@EnvironmentObject var viewModel: TodoViewModel
var body: some View {
List {
ForEach(viewModel.todos) { todo in
TodoRow(todo: todo)
}
.onDelete(perform: deleteTodo)
}
}
private func deleteTodo(at offsets: IndexSet) {
offsets.forEach { index in
let todo = viewModel.todos[index]
viewModel.dataController.container.viewContext.delete(todo)
}
viewModel.saveContext()
}
}
5. 添加 iCloud 同步(可选)
5.1 启用 iCloud 能力
- 在项目配置中勾选
iCloud并添加CloudKit容器。 - 修改
NSPersistentContainer初始化:
container = NSPersistentCloudKitContainer(name: "TodoModel")
container.viewContext.automaticallyMergesChangesFromParent = true
关键注意事项
- 线程安全:确保 Core Data 操作在正确的上下文中执行(如
viewContext主线程)。 - 错误处理:对保存失败等场景提供用户反馈。
- 数据迁移:若后续修改模型,需设计轻量级迁移方案。
最终效果
- Todo 数据在应用重启后仍然保留。
- 支持多设备同步(若启用 iCloud)。
- 完整代码示例见 GitHub 仓库链接。
