So, I want to have a Text
that changes its content based on the contents of my CoreData Model. To do that I used a computed property in Xcode beta 4 but it doesn't seem to work anymore. Either that's a bug or there is some other issue I don't see?
The problem I have exactly is that my View (and the computed property) don't seem to get updated when self.objectWillChange.send()
is called in my store.
I also tried to 'export' my var into the store and get it from there, with the same result...
EDIT:
I just tried the same with another class and it didn't work with just objectWillChange.send()
but only with @Published
however, even that stopped working if the class inherited from NSObject...
I just found out: with
struct Today: View {
@EnvironmentObject var myStore: DateStore
var hasPlans: Bool {
guard let plans = myStore.getPlans() else { return false }
return plans.isEmpty
}
var body: some View{
VStack{
Text(hasPlans ? "I have plans":"I have time today")
Button(action: {
self.myStore.addPlans(for: Date())
}) {
Text("I have plans")
}
}
}
class DateStore: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
private var fetchedResultsController: NSFetchedResultsController<DateStore>
//...
public func addPlans(for date: Date){
//{...}
if let day = self.dates.first(where: { $0.date == date}){
day.plans += 1
saveChanges()
}else{
self.create(date: dayDate)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.objectWillChange.send()
}
}
That's a very simplified version of my problem and I know that my DataModel works because the values change and self.objectWillChange.send()
is called, but my View isn't updated for some reason....
I don't see that NSObject is the source of the problem. The problem seems to be that you haven't implemented objectWillChange
. The compiler will let you get away with that, but the result is that your objectWillChange
does nothing.
Here's a simple example that shows how to configure an ObservableObject (that is an NSObject) with a computed property whose binding works:
class Thing : NSObject, ObservableObject {
let objectWillChange = ObservableObjectPublisher()
var computedProperty : Bool = true {
willSet {
self.objectWillChange.send()
}
}
}
struct ContentView: View {
@EnvironmentObject var thing : Thing
var body: some View {
VStack {
Button(self.thing.computedProperty ? "Hello" : "Goodbye") {
self.thing.computedProperty.toggle()
}
Toggle.init("", isOn: self.$thing.computedProperty).frame(width:0)
}
}
}
You can see by tapping the button and the switch that everything is live and responsive to the binding within the view.
Experimenting with my own code exhibiting similar problem.
It looks like the @Publisher SwiftUI magic breaks when the Class adopting ObservableObject is a subclass of NSObject.
The short answer solution is that if you can get it working with @Published when it is not a subclass of NSObject, then when you make it a subclass of NSObject, replace @Published with
let objectWillChange = PassthroughSubject<Void, Never>()
You'll have to import the Combine framework in your Class file to do this.
Here is some code from the working app I'm experimenting with.
The View:
import SwiftUI
struct ContentView: View {
//Bind using @ObservedObject
@ObservedObject var provider: Provider
var body: some View {
Text("\(self.provider.distance)")
}
...
}
The Class:
import Combine
class Provider: NSObject, ObservableObject {
//Instead of using @Published, use objectwillchange
//@Published var distance: CLLocationDistance = 0.0
let objectWillChange = PassthroughSubject<Void, Never>()
var distance = 0.0
...
func calculateDistance() {
...
// publish the change
self.objectWillChange.send()
self.distance = newDistance
}
}
One solution that is working is to simply make a new @State var
instead of using the computed property. However, according WWDC talks about SwiftUI this feels somewhat wrong because my 'actual state' is living in my data model and by declaring a new @State
I need to keep both of them in sync which is against the "Single Source of Truth" pattern isn't it?
来源:https://stackoverflow.com/questions/57289462/computed-nsobject-properties-in-swiftui-dont-update-the-view