Does @StateObject work for static properties, such as SwiftUI Previews?

200 Views Asked by At

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?

1

There are 1 best solutions below

0
On

Those property wrappers are only for Views.