30天学会Swift-27:SwiftUI-CoreData集成

学习目标

  • 理解Core Data的基本概念和组件。
  • 掌握如何在SwiftUI项目中集成Core Data。
  • 学习如何定义数据模型(NSManagedObject)。
  • 掌握如何使用@FetchRequest从Core Data中获取数据。
  • 学习如何执行数据操作(增、删、改)。
  • 了解NSManagedObjectContext和数据持久化。

学习内容

1. Core Data 简介

Core Data是Apple提供的一个框架,用于管理应用程序的Model层数据。它不是一个数据库,而是一个对象图管理框架,可以将对象持久化到磁盘(通常是SQLite数据库)。

1.1 Core Data 主要组件
  • NSManagedObjectModel:数据模型,定义了实体(Entity)、属性(Attribute)和关系(Relationship)。
  • NSPersistentStoreCoordinator:协调器,负责将数据模型与持久化存储(如SQLite文件)连接起来。
  • NSManagedObjectContext:管理对象上下文,是与Core Data交互的主要接口。它是一个“暂存区”,用于创建、修改、删除和获取NSManagedObject实例。
  • NSPersistentContainer:在iOS 10+中引入,简化了Core Data堆栈的设置,封装了上述三个组件。
  • NSManagedObject:Core Data中的数据记录,是实体在内存中的表示。

2. SwiftUI 项目集成 Core Data

2.1 创建项目时启用Core Data

在Xcode创建新项目时,勾选“Use Core Data”复选框,Xcode会自动为你设置好Core Data堆栈。

2.2 [YourAppName]App.swift 中的设置

Xcode会自动生成一个PersistenceController类(或类似名称),并在App结构体中注入managedObjectContext到环境中。

// Persistence.swift (Xcode自动生成)
import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "YourAppName") // 替换为你的项目名
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

// YourAppNameApp.swift
import SwiftUI

@main
struct YourAppNameApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(
                    .managedObjectContext, 
                    persistenceController.container.viewContext
                )
        }
    }
}

3. 定义数据模型 (.xcdatamodeld)

  • 打开.xcdatamodeld文件。
  • 点击“Add Entity”创建新实体(例如Task)。
  • 为实体添加属性(例如name: String, isCompleted: Bool, timestamp: Date)。
  • 在右侧面板中,将Codegen设置为Class DefinitionManual/None,然后手动创建NSManagedObject子类(如果选择Class Definition,Xcode会自动生成)。

4. 获取数据 (@FetchRequest)

@FetchRequest属性包装器用于从Core Data中获取数据,并在数据变化时自动更新视图。

import SwiftUI
import CoreData

struct TaskListView: View {
    @Environment(.managedObjectContext) private var viewContext
    
    // 获取所有Task实体,按timestamp降序排列
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \.timestamp, ascending: false)],
        animation: .default)
    private var tasks: FetchedResults<Task> // Task是你的NSManagedObject子类
    
    var body: some View {
        NavigationView {
            List {
                ForEach(tasks) {
                    task in
                    Text(task.name ?? "Unknown Task")
                }
            }
            .navigationTitle("My Tasks")
        }
    }
}

5. 数据操作 (增、删、改)

5.1 增加数据
func addTask() {
    withAnimation {
        let newTask = Task(context: viewContext)
        newTask.name = "New Task \(tasks.count + 1)"
        newTask.isCompleted = false
        newTask.timestamp = Date()
        
        do {
            try viewContext.save() // 保存到持久化存储
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}
5.2 删除数据
func deleteTask(offsets: IndexSet) {
    withAnimation {
        offsets.map { tasks[$0] }.forEach(viewContext.delete)
        
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

// 在List中使用
List {
    ForEach(tasks) {
        task in
        Text(task.name ?? "Unknown Task")
    }
    .onDelete(perform: deleteTask)
}
5.3 修改数据

直接修改NSManagedObject的属性,然后保存上下文。

func toggleTaskCompletion(task: Task) {
    task.isCompleted.toggle()
    do {
        try viewContext.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
}

// 在List中点击修改
List {
    ForEach(tasks) {
        task in
        Button(action: { toggleTaskCompletion(task: task) }) {
            HStack {
                Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                Text(task.name ?? "Unknown Task")
            }
        }
    }
}

6. NSManagedObjectContext 和数据持久化

  • viewContext是主线程的NSManagedObjectContext,用于UI操作。
  • 所有对NSManagedObject的修改都发生在NSManagedObjectContext中。
  • 调用viewContext.save()会将上下文中的更改写入持久化存储。
  • 在多线程环境下,应为每个线程创建独立的NSManagedObjectContext,并通过performperformAndWait方法进行操作,以避免线程安全问题。

7. 过滤数据 (NSPredicate)

@FetchRequest可以接受一个predicate参数来过滤数据。

// 获取所有未完成的任务
@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \.timestamp, ascending: false)],
    predicate: NSPredicate(format: "isCompleted == %@", NSNumber(value: false)),
    animation: .default)
private var incompleteTasks: FetchedResults<Task>

实践练习

  1. 构建一个简单的待办事项应用
    • 创建一个新的SwiftUI项目,并启用Core Data。
    • 定义一个TodoItem实体,包含title (String) 和 isCompleted (Bool) 属性。
    • ContentView中,使用@FetchRequest显示所有待办事项。
    • 添加一个TextField和一个Button,用于添加新的待办事项。
    • 为每个待办事项添加一个切换按钮,用于标记其完成状态。
    • 实现滑动删除功能,删除待办事项。
  2. 数据过滤与排序
    • 在待办事项应用中,添加一个PickerSegmentedPicker,允许用户选择显示“所有任务”、“已完成任务”或“未完成任务”。
    • 根据用户的选择动态修改@FetchRequestpredicate
    • 尝试添加按标题字母顺序排序的功能。

思考题

  • Core Data和UserDefaults、FileManager、SQLite等其他数据存储方式有什么区别和优缺点?
  • 在多线程环境下使用Core Data时,如何确保数据的一致性和线程安全?
  • 如何处理Core Data中的数据迁移(当数据模型发生变化时)?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值