In einer SwiftUI habe View
ich eine List
basierend auf @FetchRequest
der Anzeige von Daten einer Primary
Entität und der über eine Beziehung verbundenen Secondary
Entität. Das View
und sein List
wird korrekt aktualisiert, wenn ich eine neue Primary
Entität mit einer neuen verwandten sekundären Entität hinzufüge .
Das Problem ist, dass beim Aktualisieren des verbundenen Secondary
Elements in einer Detailansicht die Datenbank aktualisiert wird, die Änderungen jedoch nicht in der Primary
Liste berücksichtigt werden. Offensichtlich wird das @FetchRequest
nicht durch die Änderungen in einer anderen Ansicht ausgelöst.
Wenn ich danach ein neues Element in der Primäransicht hinzufüge, wird das zuvor geänderte Element endgültig aktualisiert.
Als Problemumgehung aktualisiere ich zusätzlich ein Attribut der Primary
Entität in der Detailansicht und die Änderungen werden korrekt in die Primary
Ansicht übertragen.
Meine Frage lautet: Wie kann ich ein Update für alle @FetchRequests
in SwiftUI Core Data verwandten Elemente erzwingen? Insbesondere, wenn ich keinen direkten Zugriff auf die zugehörigen Entitäten habe / @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()
}
}
ObservableObject
?Antworten:
Sie benötigen einen Publisher, der ein Ereignis über Änderungen im Kontext und eine Statusvariable in der Primäransicht generiert, um die Neuerstellung der Ansicht beim Empfang eines Ereignisses von diesem Publisher zu erzwingen.
Wichtig: Zustandsvariable muss im View Builder-Code verwendet werden, da sonst die Rendering-Engine nicht weiß, dass sich etwas geändert hat.
Hier finden Sie eine einfache Änderung des betroffenen Teils Ihres Codes, die das von Ihnen benötigte Verhalten ergibt.
quelle
RefreshView(toggle: Bool)
mit einem einzigen EmptyView in seinem Körper. VerwendenList {...}.background(RefreshView(toggle: self.refreshing))
wird funktionieren.Ich habe versucht, das primäre Objekt in der Detailansicht folgendermaßen zu berühren:
Dann wird die primäre Liste aktualisiert. Die Detailansicht muss jedoch über das übergeordnete Objekt Bescheid wissen. Dies wird funktionieren, aber dies ist wahrscheinlich nicht die SwiftUI- oder Combine-Methode ...
Bearbeiten:
Basierend auf der obigen Problemumgehung habe ich mein Projekt mit einer globalen Speicherfunktion (verwalteteObject :) geändert. Dadurch werden alle zugehörigen Entitäten berührt und alle relevanten @ FetchRequest-Daten aktualisiert.
quelle