I have a handful of model objects that come in from an external SDK so I can't change their code. They are all mutable.
I have a view that uses these objects to drive its display. When making changes to the objects, these changes aren't reflected in their view.
Here's a very simple example:
class Model {
var number: String = "One"
}
struct BrokenView: View {
let model = Model()
var body: some View {
Text(model.number)
Button("Change") {
model.number = ["One", "Two", "Three", "Four", "Five", "Six"].randomElement()!
}
}
}
This makes complete sense because Model isn't publishing its changes so there's no way for SwiftUI to know it needs to rebuild the view.
My question is how do I get SwiftUI to listen to changes in Model objects?
I've come up with two solutions, neither of which I love.
The first is to add an updater @State variable that I can toggle whenever I make the change. This actually works pretty well. I can even pass a binding down to this variable to subview and have it rebuild the whole view. Obviously this doesn't seem like a great solution.
struct HackyView: View {
let model = Model()
@State private var updater: Bool = false
var body: some View {
Text(model.number)
Button("Change") {
model.number = ["One", "Two", "Three", "Four", "Five", "Six"].randomElement()!
updater.toggle()
}
if updater {
EmptyView()
}
}
}
My next solution is wrapping each of the model classes in an ObservableObject with @Published properties. This feels a little better, but it's a lot of extra work.
struct WrapperView: View {
@StateObject var model = PublishedModel(model: Model())
var body: some View {
Text(model.number)
Button("Change") {
model.number = ["One", "Two", "Three", "Four", "Five", "Six"].randomElement()!
}
}
class PublishedModel: ObservableObject {
let model: Model
init(model: Model) {
self.model = model
self.number = model.number
}
@Published var number: String {
didSet {
model.number = number
}
}
}
}
I think my ideal solution would be some sort of extension or generic wrapper class that can make these properties @Published so the view knows they've changed. Is there any way to do that?
Here is a GitHub gist you can copy and paste into an empty Xcode project if you want to give this a try. https://gist.github.com/blladnar/4b2d1eb419151c5126c28d9da8646e92

class ObservableStuff { @Published var model = Model() }? And then insideBrokenView,@StateObject var stuff = ObservableStuff(). UseText(stuff.model.number)for the text.Modelis a class