I'm not sure if this is a bug or not, but from what I can tell, @StateObject
seems to behave like @ObservedObject
when used with static
properties.
Expected behaviour: A single instance is made, that's shared between renders of the preview, so that each press of the button increments the same integer.
Actual behaviour: It appears that a new instance is created on every render of the Preview, causing the counter to be perpetually wiped to 0. The NSLog statements confirm that this is the case.
Here's an example that illustrates the issue:
import SwiftUI
struct ContentView: View {
let title: String
@ObservedObject var state: DummyState
var body: some View {
Text("\(title): \(state.counter)").padding()
}
}
struct ContentView_Previews: PreviewProvider {
// Doesn't work as expected. Seems to act just like `@ObservedObject`,
// i.e. a new instance is made on every render.
@StateObject static var buggyState = DummyState()
// Works as expected, only 1 instance is created.
static let tmp = DummyState()
@ObservedObject static var workingState = tmp // @StateObject also works
static var previews: some View {
VStack {
ContentView(title: "Broken counter", state: buggyState) // Counter is always 0
ContentView(title: "Working counter", state: workingState)
Button {
buggyState.counter += 1;
workingState.counter += 1;
let _ = NSLog("""
DummyState
initCount: \(DummyState.initCount),
latestID: \(ObjectIdentifier(buggyState))
""")
}
label: { Text("++") }
.padding()
}.frame(width: 400, height: 200)
}
}
// This is some state I want to define in my Preview, and pass into my View to render.
class DummyState: ObservableObject {
/// Tracks how many instances of `DummyState` were ever made
static var initCount = 0
@Published var counter = 0
init() { Self.initCount += 1 }
}
As a workaround, I found that I could use a regular static
variable to hold my one DummyState()
reference, and then make a second @ObservedObject
static variable to alias it, then it works as-expected.
So, is this a bug, or am I missing something?
Those property wrappers are only for Views.