There are many questions on @FetchRequest with answers that provide workarounds for forcing SwiftUI to redraw the view. I can't find any that address this question.
My app uses a share extension to insert a new item into my CoreData
model (sqlite). A framework is shared between the main app and the share extension target. The main SwiftUI ContentView
in my app uses a @FetchRequest
property to monitor the contents of the database and update the view:
struct ContentView: View {
@FetchRequest(fetchRequest: MyObj.allFetchRequest()) var allObjs: FetchedResults<MyObj>
...
}
my xcdatamodel is extended with:
public static func allFetchRequest() -> NSFetchRequest<MyObj> {
let request: NSFetchRequest<MyObj> = MyObj.fetchRequest()
// update request to sort by name
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
return request
}
The share extension adds a new item using:
let newObj = MyObj(context: context)
newObj.name = "new name"
try! context.save()
After adding a new item using the share extension, when I return to my app by switching apps on the device, SceneDelegate
detects sceneDidBecomeActive
. In this function, I can manually run a fetch request and see that the new item has been added. I can even use answers from other SO questions to trick the UI into rebuilding in ContentView (by toggling a Bool used in the builder on receipt of a notification), but in either case, when the UI rebuilds, allObjs
does not contain the updated results from the database.
In my sceneDidBecomeActive
I have tried numerous combinations of code to attempt to update the context with the latest contents of the database in a way that the @FetchRequest
will see it. The data is there, and the my managed context can see this new data because a manual fetch request returns the new item, but the @FetchRequest
is always stale preventing a UI rebuild. In both locations I've printed the context and am sure that it is the same context. I've tried:
context.reset()
context.refreshAllObjects()
context.save()
I'm at a loss as to why the @FetchRequest
is not updating its values when ContentView
redraws.
How can I force @FetchRequest
to update its objects and cause a SwiftUI rebuild after my share extension adds a new object to the database?
(Xcode 11.4.1, Swift 5, target: iOS 13.1)
edit:
In my ContentView
, I print the allObjs
variables, their state, the memory address of the context, etc. I compare this to a new fetch from the context and all values for existing objects are the same, except the new fetch shows the newly added object, whereas allObjs
does not contain the new object. It is very clear that the @FetchRequest
object is not getting updated.
edit 2:
I tried creating a custom ObservableObject
class with a @Published
property and an explicit update()
method, then using it as an @ObservedObject
in ContentView
. This works.
class MyObjList: ObservableObject {
static let shared = MyObjList()
@Published var allObjs: [MyObj]!
init() {
update()
}
func update() {
allObjs = try! DataStore.persistentContainer.viewContext.fetch(MyObj.allFetchRequest())
}
}
Then in ContentView
:
@EnvironmentObject var allObjs: MyObjList
Which helps confirm that the data is there, it just isn't getting updated properly by @FetchRequest
. This is a functional workaround.