I have a Core Data model with an entity generated into class Task
. I am trying to get the Combine publisher objectWillChange
from the NSManagedObject
to send (automatically, without manual work), but it won't. The task entity has a name
attribute.
let task = Task(context: container.viewContext)
let taskSubscription = task.objectWillChange.sink(receiveValue: { _ in
print("Task changed")
})
task.name = "Foo" // WILL NOT trigger
If I call send manually, the subscription will work:
task.objectWillChange.send() // Will trigger
If I replace this with a simple ObservableObject
, it will work as expected:
class DummyTask: ObservableObject {
@Published var name: String?
}
let dummy = DummyTask()
let dummySubscription = dummy.objectWillChange.sink(receiveValue: { _ in
print("Dummy changed")
})
dummy.name = "Foo" // Will trigger
dummy.objectWillChange.send() // Will trigger
Is NSManagedObject bugged? How should I observe the general entity object for changes? How should I get SwiftUI to see them?
This is using Xcode 11.0 and iOS 13.
I believe it is a bug. There is no point for NSManagedObject
to conform to ObservableObject
but unable to mark any property as @Published
.
While we are waiting Apple to rectify this, I've come across a cleaner solution than @jesseSpencer's suggested one. The logic behind is the same, by adding a objectWillChange.send()
, but adding globally into willChangeValue(forKey key: String)
instead of adding into individual properties.
override public func willChangeValue(forKey key: String) {
super.willChangeValue(forKey: key)
self.objectWillChange.send()
}
Credits: https://forums.developer.apple.com/thread/121897
My guess is that it is a bug. NSManagedObject conformance to ObservableObject was added in beta 5 which also introduced other significant changes, including deprecation of BindableObject (for replacement by ObservableObject).
See the SwiftUI section: https://developer.apple.com/documentation/ios_ipados_release_notes/ios_13_release_notes)
I ran into the same issue and despite NSManagedObject conforming to ObservableObject, it was not emitting notifications for changes. This might have something to do with NSManagedObject properties needing to be wrapped with @NSManaged which cannot be combined with @Published, while the ObservableObject doc states that, by default, an ObservableObject will synthesize objectWillChange publishers for @Published property changes. https://developer.apple.com/documentation/combine/observableobject
I first tried to get around this by bootstrapping a call to objectWillChange.send()
in overrides to Key-Value methods in my NSManagedObject subclass, which only resulted in incorrect behavior.
The solution I went with is the simplest and unfortunately maybe the bulkiest if you need to change a lot of codependent properties in your SwiftUI view. But, so far it is working fine for me and maintains use of SwiftUI as intended.
In Swift:
In that subclass, create setter methods for the properties you wish to change from your SwiftUI views and at the beginning of the method add a call to objectWillChange.send()
, which should look something like this:
func setTitle(_ text: String) {
objectWillChange.send()
self.title = text
}
I only advise this as a temporary workaround, as it is not ideal and hopefully will be addressed soon.
I will be submitting a bug report in FeedbackAssistant and I recommend to anyone else encountering this issue to do the same, so we can get Apple to take another look at this!
Edit: A warning about @Anthony’s answer: While the suggested approach does work, be aware that it will not work when changing collection type relationships, i.e. adding an object to an array associated with the NSManagedObject.
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