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

使用CoreData保存闪卡数据失败,请求技术排查

问题描述

独立开发一款类似Quizlet的闪卡应用时,遇到应用无法正常运行、数据无法保存的问题。已通过DataController管理CoreData数据,实体FlashCardData包含name、term、definition、id字段;同时实现了TermDefinitionView用于用户输入闪卡信息,但当前逻辑无法完成数据持久化,尝试使用EnvironmentObject传递DataController也未生效。


代码文件:DataController.swift

import Foundation
import CoreData

class DataController: ObservableObject {
    let container = NSPersistentContainer(name: "CoreData")
    @Published var savedFlash: [FlashCardData] = []
    
    init() {
        container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data加载失败: \(error.localizedDescription)")
            }
        }
    }
    
    func fetchRequest() {
        let request = NSFetchRequest<FlashCardData>(entityName: "FlashCardData")
        do {
            savedFlash = try container.viewContext.fetch(request)
        } catch let error {
            print("数据获取失败... \(error)")
        }
    }
    
    func save(context: NSManagedObjectContext) {
        do {
            try context.save()
            print("数据保存成功")
        } catch {
            print("数据保存失败...")
        }
    }
    
    func add(name: String, term: String, definition: String, context: NSManagedObjectContext) {
        let data = FlashCardData(context: context)
        data.id = UUID()
        data.definition = definition
        data.name = name
        data.term = term
        
        save(context: context)
    }
    
    func edit(data: FlashCardData ,name: String, term: String, defintion: String, context: NSManagedObjectContext) {
        data.name = name
        data.term = term
        data.definition = defintion
        
        save(context: context)
    }
}

代码文件:TermDefinitionView.swift

import SwiftUI
import Combine
import CoreData

struct TermDefinitionView: View {
    @ObservedObject private var viewModel = TermDefinitionViewModel()
    @Binding var showNew: Bool
    @State var term = ""
    @State var definition = ""
    @State var name = ""
    @Environment (\.managedObjectContext) var managedObject
    @Environment (\.dismiss) var dismiss
    @EnvironmentObject var dataController: DataController

    var body: some View {
        ZStack {
            NavigationView {
                VStack {
                    List {
                        ForEach(viewModel.termdefpairs.indices, id: \.self) { index in
                            TermView(term: $viewModel.termdefpairs[index].term, definition: $viewModel.termdefpairs[index].definition, name: $viewModel.termdefpairs[index].name)
                        }
                        .onDelete { index in
                            self.viewModel.termdefpairs.remove(at: index.first!)
                        }
                    }
                    .navigationBarItems(trailing: Button("Save") {
                        dataController.add(name: name, term: term, definition: definition, context: managedObject)
                        dismiss()
                    })
                    Spacer()

                    Button(action: {
                        viewModel.addNew()
                    }) {
                        Image(systemName: "plus")
                            .resizable()
                            .foregroundColor(.white)
                            .font(.title2)
                            .frame(width: 30, height: 30)
                    }
                    .frame(width: 40, height: 40)
                    .background(Color.blue)
                    .clipShape(Circle())
                    .padding()
                }
            }
        }
    }
}

struct TermDefinitionView_Previews: PreviewProvider {
    static var previews: some View {
        TermDefinitionView(showNew: .constant(true))
    }
}

struct TermView: View {
    @Binding var term: String
    @Binding var definition: String
    @Binding var name: String
    
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            VStack(alignment: .leading, spacing: 8) {
                Text("Name")
                    .font(.headline)
                TextField("Enter name", text: $name)
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }
            VStack(alignment: .leading, spacing: 8) {
                Text("Term")
                    .font(.headline)
                TextField("Enter term", text: $term)
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }
            VStack(alignment: .leading, spacing: 8) {
                Text("Definition")
                    .font(.headline)
                TextField("Enter definition", text: $definition)
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }
        }
        .padding()
    }
}

struct TermAndDefinition: Identifiable {
    var id = UUID()
    var term: String
    var definition: String
    var name: String
}

代码文件:TermDefinitionViewModel.swift

import SwiftUI
import Combine

final class TermDefinitionViewModel: ObservableObject {
    @Published var termdefpairs: [TermAndDefinition] = [TermAndDefinition(term: "", definition: "", name: "")]
    
    private var dataController = DataController()

    func addNew() {
        termdefpairs.append(TermAndDefinition(term: "", definition: "", name: ""))
    }
    
    func save() {
        for pair in termdefpairs {
            dataController.add(name: pair.name, term: pair.term, definition: pair.definition)
        }
    }
}

问题分析与修复方案

核心问题点

  1. EnvironmentObject未全局注入:TermDefinitionView声明了@EnvironmentObject var dataController: DataController,但未在App入口注入全局实例,导致调用的是无效对象。
  2. 保存逻辑错位:Save按钮仅尝试保存视图中孤立的@State变量,完全忽略了用户实际输入的viewModel.termdefpairs数组内容。
  3. DataController实例不一致:TermDefinitionViewModel内部自行创建了DataController实例,和视图中通过EnvironmentObject获取的实例不是同一个,数据无法同步。
  4. 数据未主动刷新:保存数据后未调用fetchRequest(),导致savedFlash数组不更新,UI无法展示新数据。

具体修复步骤

1. 全局注入DataController

在App入口结构体中初始化DataController,并注入到环境中:

@main
struct FlashCardApp: App {
    let dataController = DataController()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(dataController)
                .environment(\.managedObjectContext, dataController.container.viewContext)
        }
    }
}

2. 修正视图保存逻辑

删除视图中无用的@State变量,修改Save按钮动作,调用ViewModel的保存方法:

// 移除这三个无用的@State变量
// @State var term = ""
// @State var definition = ""
// @State var name = ""

// 修改Save按钮
.navigationBarItems(trailing: Button("Save") {
    viewModel.save(with: dataController)
    dismiss()
})

3. 重构ViewModel的保存逻辑

移除ViewModel内部的DataController实例,改为通过参数接收全局实例,同时添加空值校验:

final class TermDefinitionViewModel: ObservableObject {
    @Published var termdefpairs: [TermAndDefinition] = [TermAndDefinition(term: "", definition: "", name: "")]

    func addNew() {
        termdefpairs.append(TermAndDefinition(term: "", definition: "", name: ""))
    }
    
    func save(with dataController: DataController) {
        for pair in termdefpairs {
            // 跳过空条目,避免无效数据
            guard !pair.name.isEmpty, !pair.term.isEmpty, !pair.definition.isEmpty else {
                print("跳过空闪卡条目")
                continue
            }
            dataController.add(name: pair.name, term: pair.term, definition: pair.definition, context: dataController.container.viewContext)
        }
    }
}

4. 完善DataController的自动刷新

在save方法中添加fetchRequest(),确保保存后自动更新数据列表:

func save(context: NSManagedObjectContext) {
    do {
        try context.save()
        print("数据保存成功")
        fetchRequest() // 保存后自动刷新数据
    } catch {
        print("数据保存失败: \(error.localizedDescription)")
    }
}

5. 修复预览视图

给预览添加DataController环境注入:

struct TermDefinitionView_Previews: PreviewProvider {
    static var previews: some View {
        TermDefinitionView(showNew: .constant(true))
            .environmentObject(DataController())
            .environment(\.managedObjectContext, DataController().container.viewContext)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 02:35:00