The problem that I am having is that when I update a core data Asset object from a sheet view, the change is not reflected in the UI of the AssetListView. (Note that Inserting a new object from the sheet view does refresh the UI of the AssetListView. Deleting an object in sheet view also refreshes the UI of the AssetListView) The only action that is not working is the Update.
How can I make the AssetListView change when the core data object changes?
I have the following SwiftUI code showing a list of assets from a CoreData FetchRequest:
struct AssetListView: View {
@State private var showingSheet = false
@State private var selectedAssetId: NSManagedObjectID?
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Asset.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Asset.allocationPercentage, ascending: false)]) var assets: FetchedResults<Asset>
var body: some View {
VStack {
Form {
ForEach(assets, id: \.self) { asset in
Section {
AssetRowView(asset: asset)
.onTapGesture {
self.selectedAssetId = asset.objectID
self.showingSheet = true
}
}
}
}
}
.navigationBarTitle("Assets").sheet(isPresented: $showingSheet ) {
EditAssetView(assetId: self.selectedAssetId!)
.environment(\.managedObjectContext, self.moc)
}
}
}
}
And this is an edit screen, which I present as SwiftUI sheet:
struct EditAssetView: View {
var assetId: NSManagedObjectID
@Environment(\.presentationMode) var presentationMode
@State private var name = ""
@State private var description = ""
@Environment(\.managedObjectContext) var moc
var asset: Asset {
moc.object(with: assetId) as! Asset
}
var body: some View {
NavigationView {
Form {
Section {
TextField("Name", text: $name)
TextField("Description", text: $description)
}
}
.navigationBarTitle(Text("Edit Asset"), displayMode: .inline)
.navigationBarItems(leading: Button("Cancel") {
self.presentationMode.wrappedValue.dismiss()
}, trailing: Button("Done") {
self.presentationMode.wrappedValue.dismiss()
self.asset.name = self.name
self.asset.assetDescription = self.description
try? self.moc.save()
}
)
}
.onAppear {
self.name = self.asset.name
self.description = self.asset.assetDescription
}
}
}
Here is the code for AssetRowView:
struct AssetRowView: View {
var asset: Asset?
var body: some View {
HStack {
Text(asset.name)
Text(asset.assetDescription)
}
}
}
Try to observe Asset
(cause NSManagedObject
is-a ObservableObject
)
struct AssetRowView: View {
@ObservedObject var asset: Asset // << here !!
var body: some View {
HStack {
Text(asset.name)
Text(asset.assetDescription)
}
}
}
if that will not be enough, also might be needed to update Done
button action as
}, trailing: Button("Done") {
self.asset.objectWillChange.send() // << here !!
self.asset.name = self.name
self.asset.assetDescription = self.description
try? self.moc.save()
// better do this at the end of activity
self.presentationMode.wrappedValue.dismiss()
}
What I did is pass the core data object as:
@ObservedObject var toDo: FetchedResults<ToDoModelCoreData>.Element
from my fetch result
@FetchRequest var todoFetchRequest: FetchedResults<ToDoModelCoreData>
and that will make sure swiftUI view is updated when coredata it is
adding @ObservedObject
to the core data object works, but you may need to scroll the list to see the change. depending on how you set up your predicate, managed objects, and fetch request.
adding foo.objectWillChange.send()
at the end of whatever process edits the NSManagedObject fixes this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With