Je viens de terminer la partie du cours SwiftUI sur Core Data et j’ai décidé d’aller un peu plus loin en créant une vue séparée pour entrer les informations d’une nouvelle « tâche » dans la liste.
Le problème c’est que, malgré le fait que ma nouvelle tâche soit bien enregistrée dans Core Data, la vue contenant la liste ne se met pas à jour en temps réel. Je suis obligé de fermer la preview et de la réouvrir pour que mon nouvel item apparaisse.
Une idée de ce qui coince dans mon code ?
Le code de ma vue qui gère l’affichage de la liste de to do:
import SwiftUI
import CoreData
struct BrainDumpView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State var dumplist = DumpManager().dumpList
@State var dumpManager = DumpManager()
@State private var showingAddDumpView: Bool = false
// @State var newDumpName: String
var body: some View {
NavigationView {
VStack {
VStack(alignment: HorizontalAlignment.leading){
List{
ForEach(dumplist) { dump in
Text(dump.name)
}
.onDelete(perform: deleteDump)
.onTapGesture {
updateDump()
}
}
Spacer()
}
}
.navigationBarTitle(Text("Brain Dumps"))
.navigationBarItems(trailing:
Button(action:
showingAddDump
) {
Image(systemName: "plus")
.font(.largeTitle)
}
)
}
.sheet(isPresented: $showingAddDumpView, content: {
NewDumpView(newDumpName: "")
})
}
func showingAddDump() {
showingAddDumpView.toggle()
}
// func createNewDump() {
// if newDumpName.count > 0 {
// dumpManager.addDump(dumpName: newDumpName)
// }
// newDumpName = ""
// }
func deleteDump(offsets: IndexSet){
dumpManager.deleteDump(withID: dumpManager.dumpList[offsets[offsets.startIndex]].id)
}
}
func updateDump(){
}
struct BrainDumpView_Previews: PreviewProvider {
static var previews: some View {
BrainDumpView()
}
}```
Et le code de la vue qui apparaît en tant que sheet:
C’est fait, mais j’ai toujours une erreur au niveau de la preview, à la fois dans BrainDumpView et dans NewDumpView, quoi que je passe comme binding, j’ai ce message d’erreur :
Tu as été plus rapide que moi, j’étais en train de modifier ma question !
C’est fait, cependant ma liste de tâches ne se reload pas en temps réel avec la nouvelle tâche ajoutée, je dois à chaque fois rebuilder pour que ça aille fetch la nouvelle tâche dans Core Data. Est-ce qu’il y a une erreur ou un élément manquant dans mon forEach ?
Est-ce que tu testes sur ton iPhone ? (par opposition à test sur simulateur ou via les previews)
Si oui, deux pistes:
1- vérifier que, quand tu ajoutes un élément, la dumpList est réellement mise à jour
2- Passer par une architecture différente, où tu utilises un @FetchResult comme source de données (voir CoreData Many-to-Many et mise à jour des données comme exemple de conversation récente)
Pour la piste 1, n’hésite pas à reposter ton code actuel
import Foundation
import SwiftUI
class DumpManager: ObservableObject {
let storage: CoreDataStorage = CoreDataStorage()
@Published var dumpList: [Dump]
init(){
dumpList = storage.fetchTaskList()
}
}
En fait on dirait que l’ajout d’une nouvelle tâche dans la liste ne trigger pas un refresh de la dumpList, malgré le fait qu’elle est publiée et qu’elle dépend d’une fonction qui utilise bien fetch comme tu le mentionnes:
func fetchTaskList() -> [Dump] {
let dumpList: [Dump]
let fetchRequest: NSFetchRequest<CDDump> = CDDump.fetchRequest()
if let rawDumPlist = try? context.fetch(fetchRequest){
dumpList = rawDumPlist.compactMap({(rawDump:CDDump) -> Dump? in
Dump(fromCoreDataObject: rawDump)
})
}else {
dumpList = []
}
return dumpList
}
Et dans ma vue principale, voici ce qui est déclaré:
struct BrainDumpView: View {
@ObservedObject var dumpManager: DumpManager = DumpManager()
@State var theFuckingList = DumpManager().dumpList
@State private var showingAddDumpView: Bool = false
// @State var newDumpName: String
var body: some View {
NavigationView {
VStack {
VStack(alignment: HorizontalAlignment.leading){
List{
ForEach(theFuckingList) { dump in
Text(dump.name)
}
//
// NewDumpView.swift
// NewDumpView
//
// Created by Charlie Maréchal on 2021-07-23.
//
import SwiftUI
struct NewDumpView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var dumpManager: DumpManager = DumpManager()
@State var newDumpName: String
@Binding var dumpList: [Dump]
var body: some View {
NavigationView {
VStack{
Spacer()
TextField("Dump", text: $newDumpName, onCommit: createNewDump)
.padding()
.background(Color(UIColor.tertiarySystemFill))
.cornerRadius(9)
.font(.system(size: 24, weight: .bold, design: .default))
.navigationBarTitle("New Dump", displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "xmark")
}
)
Button(action: {
self.presentationMode.wrappedValue.dismiss()
createNewDump()
}) {
Text("Save")
}
}
}
.padding()
.navigationViewStyle(StackNavigationViewStyle())
}
func createNewDump() {
if newDumpName.count > 0 {
dumpManager.addDump(dumpName: newDumpName)
}
newDumpName = ""
self.presentationMode.wrappedValue.dismiss()
}
}
// struct NewDumpView_Previews: PreviewProvider {
// @ObservedObject var dumpManager: DumpManager = DumpManager()
// @Binding var dumpList: [Dump]
// static var previews: some View {
//
// NewDumpView(newDumpName: "", dumpList: $dumpList)
// }
// }
Si j’arrête la build et que je reload, je vois que les éléments s’ajoutent et s’effacent correctement de la base de données Core Data, mais ils ne s’ajoutent pas à la vue en temps réel, ni dans le simulateur, ni sur mon iPhone, donc je suppose que le binding est problématique au niveau de la génération de la liste, qui ne reflète pas les changements en temps réel.
Une (ou la) source du problème est que dans NewDumpView, tu recrées une nouvelle instance de DumpManager; c’est cette instance que tu vas mettre à jour alors que l’instance de DumpManager utilisée par ta vue BrainDumpView ne sera pas mis à jour.
Il faut que, quand tu ajoutes un Dump, tu mettes à jour la variable dumpList passée en @Binding dans NewDumpView, ce qui permettra à BrainDumpView d’en tenir compte.
Dans ton code actuel, tu mets à jour une nouvelle variable dumpList qui n’est pas reliée à BrainDumpView, il n’y a donc aucune chance que BrainDumpView puisse s’en apercevoir.
Pas le temps de te dire quelque chose de plus précis à ce stade !
Cordialement,
Nicolas
En complément, pour éviter tout problème de ce type, supprime dans BrainDumpView
@State var theFuckingList = DumpManager().dumpList
Merci Tout fonctionne correctement maintenant ! Effectivement j’avais mal compris la relation entre les différents éléments, notamment le fait que je créais à chaque fois de nouvelles instances !