代码之家  ›  专栏  ›  技术社区  ›  Vincent

SwiftUI与表单中的CoreData绑定

  •  0
  • Vincent  · 技术社区  · 1 年前

    我有一个具有多个属性的核心数据模型和一个用于编辑所有这些属性的表单。
    我希望更改是“自动保存”的。因此,一旦我更改了表单中的某些内容,它就会保存到我的核心数据上下文中。

    我发现自己做了很多自定义绑定来保存更改时的上下文,例如:

    TextField("My string", 
      text: Binding(
        get: {
            model.myString
        }, 
        set: { newValue in
            model.myString = newValue
            try? context.save()
        }
      )
    )
    

    我想简化我的代码,并将一个特殊的绑定绑定到核心数据上下文,以避免这种样板。我该如何简化它?

    1 回复  |  直到 1 年前
        1
  •  1
  •   lorem ipsum    1 年前

    如果你有

    @Environment(\.managedObjectContext) var context
    @ObservedObject var model: SomeType
    

    在视图的顶部

    您可以使用

     .task(id: model.hasChanges) {
          guard model.hasChanges else {return}
          try? context.save()
      }
    

    只要有变化,它就会保存下来。

    你可以把这个放在 ViewModifier 以便在您想要自动保存的任何位置轻松访问。

    import SwiftUI
    import CoreData
    struct SimpleItemListView: View {
        @FetchRequest( sortDescriptors: []) var items: FetchedResults<SimpleItem>
        var body: some View {
            List(items) { item in
                Text(item.name ?? "")
                    .autoSave(item) // Use like this.
            }
        }
    }
    
    #Preview {
        SimpleItemListView()
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
    
    //MARK: AutoSave ViewModifier just copy & paste.
    
    extension View {
        func autoSave<MO>(_ object: MO) -> some View where MO: NSManagedObject  {
            modifier(AutoSave(object: object))
        }
    }
    struct AutoSave<MO>: ViewModifier where MO: NSManagedObject {
        @Environment(\.managedObjectContext) var managedObjectContext
        @ObservedObject var object: MO
        @State private var alert: (isPresented: Bool, error: LocalError?) = (false, nil)
        func body(content: Content) -> some View {
            content
                .alert(isPresented: $alert.isPresented, error: alert.error, actions: {
                    Button("Ok") {
                        alert = (false, nil)
                    }
                })
                .task(id: object.hasChanges) {
                    guard object.hasChanges else {return}
                    do {
                        try await managedObjectContext.perform {
                            try managedObjectContext.save()
                        }
                    } catch {
                        alert = (true, LocalError.error(error))
                    }
                }
        }
        private enum LocalError: LocalizedError {
            case error(Error)
            
            var errorDescription: String?{
                switch self {
                case .error(let error):
                    return error.localizedDescription
                }
            }
        }
    }