I have two core data entities, a Deck and and Card. The Deck has a relationship with Card (one Deck to many Card). In SwiftUI after changing the attribute of Card in a different screen, the list of Card does not update its view to reflect the change.
struct DeckDetailView: View {
@ObservedObject var deck: Deck
@State private var showSheet : Bool = false
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
Form{
ForEach(deck.cards?.allObjects as? [Card] ?? []){ card in
NavigationLink {
CardDetailView(card: card) { returnedCard in
updateCard(card: returnedCard)
}
} label: {
Text("Question: \(card.question)")
}
}
}
.navigationTitle("\(deck.name)")
.toolbar{
Button {
showSheet.toggle()
} label: {
Label("Add Deck", systemImage: "plus")
}
.sheet(isPresented: $showSheet) {
NavigationStack{
AddCardView { cardData in
addCard(cardData: cardData)
}
}
}
}
}
private func updateCard(card: Card){
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
func addCard(cardData: CardSO){
let card = Card(context: viewContext)
card.id = UUID().uuidString
card.question = cardData.question
card.answer = cardData.answer
deck.addToCards(card)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
Excellent question! I have faced this same issue many times. One of the ways, I ended up solving the issue is by using @FetchRequest property wrapper. I would pass Deck to the detail screen but use the Deck object to perform a new FetchRequest to get all the cards. When you use @FetchRequest, it will automatically track the changes.
I wrote a similar article on this issue that you can check out below: https://azamsharp.com/2023/01/30/active-record-pattern-swiftui-core-data.html. See the section "Creating, Updating and Reading Reminders".
struct MyListDetailView: View {
let myList: MyList
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: [])
private var reminderResults: FetchedResults<Reminder>
init(myList: MyList) {
self.myList = myList
_reminderResults = FetchRequest(fetchRequest: Reminder.byList(myList: myList))
}
extension Reminder: Model {
static func byList(myList: MyList) -> NSFetchRequest<Reminder> {
let request = Reminder.fetchRequest()
request.sortDescriptors = []
request.predicate = NSPredicate(format: "list = %@", myList)
return request
}
}