В SwiftUI у View
меня есть List
основанный на @FetchRequest
показе данных Primary
объекта и Secondary
объекта, связанного через отношения . View
И его List
обновляется корректно, когда я добавить новый Primary
объект с новым связанным с вторичным объектом.
Проблема в том, что когда я обновляю подключенный Secondary
элемент в подробном представлении, база данных обновляется, но изменения не отражаются в Primary
списке. Очевидно, что @FetchRequest
это не вызвано изменениями в другом представлении.
Когда я добавляю новый элемент в основной вид после этого, ранее измененный элемент в конце концов обновляется.
В качестве обходного пути я дополнительно обновляю атрибут Primary
сущности в подробном представлении, и изменения корректно распространяются на Primary
представление.
У меня вопрос: как я могу принудительно обновить все связанные @FetchRequests
с SwiftUI Core Data? Особенно, когда у меня нет прямого доступа к связанным объектам / @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
?Ответы:
Вам нужен Publisher, который генерировал бы событие об изменениях в контексте и некоторую переменную состояния в первичном представлении, чтобы принудительно перестроить представление при получении события от этого издателя.
Важно: переменная состояния должна использоваться в коде конструктора представлений, иначе механизм визуализации не будет знать, что что-то изменилось.
Вот простая модификация уязвимой части вашего кода, которая дает поведение, которое вам нужно.
источник
RefreshView(toggle: Bool)
с одним EmptyView в своем теле. ИспользованиеList {...}.background(RefreshView(toggle: self.refreshing))
будет работать.Я попытался прикоснуться к первичному объекту в подробном представлении следующим образом:
Тогда основной список будет обновляться. Но подробный вид должен знать о родительском объекте. Это будет работать, но это, вероятно, не SwiftUI или Combine ...
Редактировать:
Основываясь на вышеупомянутом обходном пути, я изменил свой проект с помощью глобальной функции save (managedObject :). Это коснется всех связанных сущностей, тем самым обновляя все соответствующие @ FetchRequest's.
источник