You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SwiftUI TCA与CoreData的待办APP:任务完成状态切换实现

基于TCA+CoreData实现任务完成状态切换与UI同步

核心优化方向

现有实现通过重新加载全部任务来更新UI,功能可行但效率偏低。更贴合TCA设计理念的做法是:将CoreData实体转为值类型模型存入State,修改状态后直接更新对应项,避免全量刷新,同时保证状态变化能被TCA精准捕获。

1. 定义值类型任务模型(TCA最佳实践)

CoreData的Tasks是引用类型,不适合直接存入TCA的State。先定义一个值类型的Task模型,用于状态管理:

struct Task: Equatable, Identifiable {
    let id: UUID
    var name: String
    var isCompleted: Bool
    
    // 从CoreData实体转换为值类型模型
    init(from coreDataTask: Tasks) {
        self.id = coreDataTask.id ?? UUID()
        self.name = coreDataTask.name ?? ""
        self.isCompleted = coreDataTask.completed
    }
}

2. 调整Reducer的State与Action

修改MainViewModel的State,改用值类型模型存储任务列表;同时更新Action的关联值类型:

struct MainViewModel: ReducerProtocol {
    // 闭包改为返回值类型数组
    let loadElements: () -> [Task]

    struct State: Equatable {
        var showForm: Bool = false
        var buttonIcon: String { showForm ? "minus" : "plus" }
        var tasks: [Task] = [] // 存储值类型模型
    }

    enum Action: Equatable {
        case showHideForm
        case loadElement
        case addElement(String)
        case toggleCompleted(Task) // 接收值类型Task
    }

    func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
        switch action {
        case .showHideForm:
            state.showForm.toggle()
            return .none
        case .loadElement:
            state.tasks = loadElements()
            return .none
        case .addElement(let name):
            StorageContainer.shared.addElement(event: name)
            return .concatenate([.init(value: .showHideForm), .init(value: .loadElement)])
        case .toggleCompleted(let task):
            // 1. 更新CoreData中的任务状态
            StorageContainer.shared.toggleTaskCompletion(taskId: task.id)
            // 2. 直接更新State中对应的任务,触发UI刷新
            if let index = state.tasks.firstIndex(of: task) {
                state.tasks[index].isCompleted.toggle()
            }
            return .none // 无需重新加载全部任务
        }
    }
}

3. 优化CoreData的更新方法

新增通过ID定位任务并切换完成状态的方法:

// StorageContainer中的方法
func toggleTaskCompletion(taskId: UUID) {
    let fetchRequest: NSFetchRequest<Tasks> = Tasks.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "id == %@", taskId as CVarArg)
    
    guard let task = try? container.viewContext.fetch(fetchRequest).first else { return }
    task.completed.toggle()
    save()
}

func save() {
    do {
        try container.viewContext.save()
    } catch {
        print("保存失败:\(error)")
    }
}

4. 视图层适配调整

修改列表渲染逻辑,使用值类型模型,并确保TaskCell接收值类型数据:

// 视图中的列表部分
ForEach(vm.tasks) { task in
    TaskCell(task: task)
        .onTapGesture {
            vm.send(.toggleCompleted(task), animation: .easeInOut)
        }
        .listRowBackground(Color.clear)
        .listRowSeparator(.hidden)
}

// TaskCell示例
struct TaskCell: View {
    let task: Task
    
    var body: some View {
        HStack {
            Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                .foregroundColor(task.isCompleted ? .green : .gray)
            Text(task.name)
                .strikethrough(task.isCompleted)
            Spacer()
        }
        .padding()
    }
}

关键优势

  • 更贴合TCA设计:值类型状态确保TCA能精准检测变化,触发UI更新,避免引用类型带来的状态模糊问题。
  • 性能提升:无需全量加载任务,仅更新单个任务项,UI响应更流畅。
  • 可维护性增强:数据转换层清晰,CoreData操作与状态管理解耦,便于后续扩展。

内容的提问来源于stack exchange,提问作者Fabio Cirruto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 15:07:54