Em um SwiftUI, View
tenho como List
base a @FetchRequest
exibição de dados de uma Primary
entidade e a Secondary
entidade conectada via relacionamento . O View
e seu List
são atualizados corretamente quando adiciono uma nova Primary
entidade a uma nova entidade secundária relacionada.
O problema é que, quando atualizo o Secondary
item conectado em uma exibição detalhada, o banco de dados é atualizado, mas as alterações não são refletidas na Primary
Lista. Obviamente, o @FetchRequest
não é acionado pelas alterações em outra Visualização.
Quando adiciono um novo item na exibição principal a partir de então, o item alterado anteriormente é finalmente atualizado.
Como solução alternativa, atualizo adicionalmente um atributo da Primary
entidade na visualização de detalhes e as alterações são propagadas corretamente para a Primary
Visualização.
Minha pergunta é: Como forçar uma atualização em todos os @FetchRequests
itens relacionados no SwiftUI Core Data? Especialmente quando não tenho acesso direto às 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
?Respostas:
Você precisa de um Publicador que gere eventos sobre alterações no contexto e alguma variável de estado na exibição principal para forçar a reconstrução da exibição no evento de recebimento desse publicador.
Importante: a variável de estado deve ser usada no código do construtor de visualizações, caso contrário, o mecanismo de renderização não saberia que algo mudou.
Aqui está uma modificação simples da parte afetada do seu código, que fornece o comportamento necessário.
fonte
RefreshView(toggle: Bool)
com um único EmptyView em seu corpo. UsarList {...}.background(RefreshView(toggle: self.refreshing))
funcionará.Tentei tocar no objeto principal na exibição de detalhes assim:
Em seguida, a lista principal será atualizada. Mas a visualização de detalhes precisa saber sobre o objeto pai. Isso funcionará, mas provavelmente não é a maneira SwiftUI ou Combine ...
Editar:
Com base na solução alternativa acima, modifiquei meu projeto com uma função global save (managedObject :). Isso tocará todas as entidades relacionadas, atualizando todos os @ FetchRequest relevantes.
fonte