当 SwiftUI 中的相关实体发生更改时,如何更新 @FetchRequest? [英] How to update @FetchRequest, when a related Entity changes in SwiftUI?

查看:14
本文介绍了当 SwiftUI 中的相关实体发生更改时,如何更新 @FetchRequest?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 SwiftUI View 我有一个基于 @FetchRequestList 显示 Primary 实体的数据和通过关系连接 Secondary 实体.View 及其 List 正确更新,当我添加一个新的 Primary 实体和一个新的相关次要实体时.

In a SwiftUI View i have a List based on @FetchRequest showing data of a Primary entity and the via relationship connected Secondary entity. The View and its List is updated correctly, when I add a new Primary entity with a new related secondary entity.

问题是,当我在详细视图中更新连接的 Secondary 项时,数据库会更新,但更改未反映在 Primary 列表中.显然,@FetchRequest 不会被另一个视图中的更改触发.

The problem is, when I update the connected Secondary item in a detail view, the database gets updated, but the changes are not reflected in the Primary List. Obviously, the @FetchRequest does not get triggered by the changes in another View.

此后当我在主视图中添加新项目时,之前更改的项目最终会得到更新.

When I add a new item in the primary view thereafter, the previously changed item gets finally updated.

作为一种变通方法,我在详细视图中另外更新了 Primary 实体的属性,并将更改正确传播到 Primary 视图.

As a workaround, i additionally update an attribute of the Primary entity in the detail view and the changes propagate correctly to the Primary View.

我的问题是:如何强制更新 SwiftUI 核心数据中所有相关的 @FetchRequests?特别是,当我无法直接访问相关实体时/@Fetchrequests?

My question is: How can I force an update on all related @FetchRequests in SwiftUI Core Data? Especially, when I have no direct access to the related entities/@Fetchrequests?

import SwiftUI

extension Primary: Identifiable {}

// Primary View

struct PrimaryListView: View {
    @Environment(.managedObjectContext) var context

    @FetchRequest(
        entity: Primary.entity(),
        sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
    )
    var fetchedResults: FetchedResults<Primary>

    var body: some View {
        List {
            ForEach(fetchedResults) { primary in
                NavigationLink(destination: SecondaryView(primary: primary)) {
                VStack(alignment: .leading) {
                    Text("(primary.primaryName ?? "nil")")
                    Text("(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
                }
                }
            }
        }
        .navigationBarTitle("Primary List")
        .navigationBarItems(trailing:
            Button(action: {self.addNewPrimary()} ) {
                Image(systemName: "plus")
            }
        )
    }

    private func addNewPrimary() {
        let newPrimary = Primary(context: context)
        newPrimary.primaryName = "Primary created at (Date())"
        let newSecondary = Secondary(context: context)
        newSecondary.secondaryName = "Secondary built at (Date())"
        newPrimary.secondary = newSecondary
        try? context.save()
    }
}

struct PrimaryListView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        return NavigationView {
            PrimaryListView().environment(.managedObjectContext, context)
        }
    }
}

// Detail View

struct SecondaryView: View {
    @Environment(.presentationMode) var presentationMode

    var primary: Primary

    @State private var newSecondaryName = ""

    var body: some View {
        VStack {
            TextField("Secondary name:", text: $newSecondaryName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
                .onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
            Button(action: {self.saveChanges()}) {
                Text("Save")
            }
            .padding()
        }
    }

    private func saveChanges() {
        primary.secondary?.secondaryName = newSecondaryName

        // TODO: ❌ workaround to trigger update on primary @FetchRequest
        primary.managedObjectContext.refresh(primary, mergeChanges: true)
        // primary.primaryName = primary.primaryName

        try? primary.managedObjectContext?.save()
        presentationMode.wrappedValue.dismiss()
    }
}

推荐答案

我也为此苦苦挣扎,并找到了一个非常好的和干净的解决方案:

I also struggled with this and found a very nice and clean solution:

您必须将行包装在单独的视图中,并在实体的该行视图中使用 @ObservedObject.

You have to wrap the row in a separate view and use @ObservedObject in that row view on the entity.

这是我的代码:

酒单:

struct WineList: View {
    @FetchRequest(entity: Wine.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: Wine.name, ascending: true)
        ]
    ) var wines: FetchedResults<Wine>

    var body: some View {
        List(wines, id: .id) { wine in
            NavigationLink(destination: WineDetail(wine: wine)) {
                WineRow(wine: wine)
            }
        }
        .navigationBarTitle("Wines")
    }
}

WineRow:

struct WineRow: View {
    @ObservedObject var wine: Wine   // !! @ObserveObject is the key!!!

    var body: some View {
        HStack {
            Text(wine.name ?? "")
            Spacer()
        }
    }
}

这篇关于当 SwiftUI 中的相关实体发生更改时,如何更新 @FetchRequest?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆