En un SwiftUI, View
tengo una List
base para @FetchRequest
mostrar los datos de una Primary
entidad y la Secondary
entidad conectada a través de la relación . El View
y List
se actualiza correctamente cuando agrego una nueva Primary
entidad con una nueva entidad secundaria relacionada.
El problema es que cuando actualizo el Secondary
elemento conectado en una vista detallada, la base de datos se actualiza, pero los cambios no se reflejan en la Primary
Lista. Obviamente, el @FetchRequest
no se activa por los cambios en otra Vista.
Cuando agrego un nuevo elemento en la vista principal a partir de entonces, el elemento previamente modificado finalmente se actualiza.
Como solución alternativa, actualizo adicionalmente un atributo de la Primary
entidad en la vista de detalles y los cambios se propagan correctamente a la Primary
Vista.
Mi pregunta es: ¿Cómo puedo forzar una actualización en todo lo relacionado @FetchRequests
con SwiftUI Core Data? Especialmente, cuando no tengo acceso directo a las entidades relacionadas / @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
?Respuestas:
Necesita un publicador que genere eventos sobre cambios en el contexto y alguna variable de estado en la vista principal para forzar la reconstrucción de la vista en el evento de recepción de ese editor.
Importante: la variable de estado debe usarse en el código del generador de vistas, de lo contrario el motor de representación no sabría que algo ha cambiado.
Aquí hay una modificación simple de la parte afectada de su código, que proporciona el comportamiento que necesita.
fuente
RefreshView(toggle: Bool)
con un solo EmptyView en su cuerpo. UsarList {...}.background(RefreshView(toggle: self.refreshing))
funcionará.Traté de tocar el objeto primario en la vista de detalle de esta manera:
Entonces la lista primaria se actualizará. Pero la vista de detalle tiene que saber sobre el objeto padre. Esto funcionará, pero probablemente esta no sea la forma SwiftUI o Combine ...
Editar:
Basado en la solución anterior, modifiqué mi proyecto con una función de guardar global (managedObject :). Esto tocará todas las Entidades relacionadas, actualizando así todas las @ FetchRequest relevantes.
fuente