【问题标题】:How do I implement a child context (CoreData) in SwiftUI environment?如何在 SwiftUI 环境中实现子上下文(CoreData)?
【发布时间】:2020-07-13 14:27:44
【问题描述】:

我目前正在学习 SwiftUI,以进一步将其包含在我的日常基于 UIKit 的工作流程中。我已经到了无法在 SwiftUI 中运行一个概念的地步,而我已经在 UIKit 中使用了多年。

这个想法是使用我的主要 CoreData managedObjectContext 的子上下文来编辑实体。我基本上通过执行以下操作来实现它:

// Get view context of application
let viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

// Create NSManagedObjectContext
let editingContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

// Set the parent of the editing context to the main view context
editingContext.parent = viewContext

通过使用单独的editingContext,我可以对其中的实体进行更改,而无需直接对我的主要上下文进行更改。如果用户选择中止更改,我只需重置editingContext

对于在 SwiftUI 中的实现,我选择通过创建自定义EnvironmentKey 将editingContext 实现为环境对象:

struct EditingContextKey: EnvironmentKey {
    static let defaultValue: NSManagedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
}

extension EnvironmentValues {
    var editingContext: NSManagedObjectContext {
        get {
            return self[EditingContextKey.self]
        }
        set {
            self[EditingContextKey.self] = newValue
        }
    }
}

然后将editingContext 添加到我的根视图中:

let contentView = ContentView()
    .environment(\.managedObjectContext, viewContext)
    .environment(\.editingContext, editingContext)

到目前为止,一切正常。我还可以通过在相应视图中调用以下命令来使用editingContext

@Environment(\.editingContext) var editingContext

但是,当我对 editingContext 中的实体进行更改并尝试保存 editingContext 时,我会收到一个意外错误:

Fatal error: Unresolved error Error Domain=Foundation._GenericObjCError Code=0 "(null)", [:]: file

我已经检查过,编辑实体的上下文是否与editingContext 匹配。情况似乎如此。我有感觉,它可能与 SwiftUI 中的绑定有关,但不知道如何找到解决方案。

有人遇到过类似的问题吗?或者我对所需功能的基本方法是错误的,有更方便的方法吗?

谢谢!

【问题讨论】:

  • 感谢 Lars 提供了这个不错的解决方案。我自己试过了,效果很好。保存editingContext的内容时我没有任何崩溃。在 iOS 和 macOS 上测试。您是否有一个更完整的示例项目可以看到此问题? (例如基于 Apples CoreData 模板)

标签: ios swift core-data swiftui


【解决方案1】:

我正在使用这样的子上下文:

import SwiftUI
import CoreData

struct EditorConfig {
    var context: NSManagedObjectContext
    var isPresented = false
    var book: Book
    
    init() {
        context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        book = Book(context: context)
    }
    
    mutating func present(viewContext: NSManagedObjectContext) {
        context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        context.parent = viewContext
        book = Book(context: context)
        isPresented = true
    }
    
    mutating func dismiss() {
        isPresented = false
    }
    
    mutating func save() {
        do {
            try context.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        dismiss()
    }
}

struct ContentView: View {
    
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Book.entity(), sortDescriptors: []) var books:FetchedResults<Book>
    
    @State private var config = EditorConfig()
    
    var body: some View {
        NavigationView {
            Text("Count: \(books.count)")
            .navigationBarTitle("Bookworm")
            .navigationBarItems(trailing:
                Button(action: presentEditor){
                    Image(systemName: "plus")
                }
            )
            .sheet(isPresented: $config.isPresented){
                ContentView2(config: $config)
            }
        }
    }
    
    private func presentEditor() {
        config.present(viewContext: moc)
    }
}

struct ContentView2: View {
    
    @Binding var config: EditorConfig
    
    var body: some View {
        NavigationView{
            Form{
                ContentView3(book: config.book)
                Section{
                    Button("Save"){
                        config.save()
                    }
                }
            }
            .navigationBarTitle("Add Book")
            .navigationBarItems(trailing: Button(action: dismiss){
                    Text("Done")
                    .bold()
                }
            )
        }
    }
    
    func dismiss() {
        config.dismiss()
    }
}


struct ContentView3: View {
    
    @ObserveredObject var book: Book
    
    var body: some View {        
         Section{
             TextField("Title", text:Binding($book.title)!)
             TextField("Author's name", text:Binding($book.author)!)
         }
    }
}

【讨论】:

    猜你喜欢
    • 2020-05-05
    • 2020-03-28
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-08
    相关资源
    最近更新 更多