Should we manually call @StateObject initializer

⋅ 3 min read ⋅ SwiftUI Data Property Wrapper

Table of Contents

In the previous article, How to initialize @StateObject with parameters in SwiftUI, I share how to initialize @StateObject with parameters by using a default value and onAppear modifier.

But there is another controversial way to initialize @StateObject, that is, initialize it in an initializer using the StateObject(wrappedValue:).

This article will discuss whether we should use this format or not.

Here is what the solution looks like.

class DashboardViewModel: ObservableObject {
@Published var greeting: String

init(name: String) {
greeting = "Hello, \(name)!"
}
}

struct DashboardView: View {
@StateObject private var viewModel: DashboardViewModel

init(name: String) {
_viewModel = StateObject(wrappedValue: DashboardViewModel(name: name))
}

var body: some View {
Text("Greeting: \(viewModel.greeting)")
}
}

What is the problem with StateObject(wrappedValue:)

The reason that I didn't include this method in the previous post is because of this statement in the Apple documentation of StateObject.

You don't call this initializer directly. Instead, declare a property with the @StateObject attribute in a View, App, or Scene, and provide an initial value:

struct MyView: View {
@StateObject var model = DataModel()

// ...
}

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Should we initialize @StateObject in Initializer

Even though Apple said not to use it, I know many people use StateObject(wrappedValue:) to initialize @StateObject variable. I also use it sometimes, but what is written in the documentation keeps bugging me.

Finally, I found the answer that ends my suffering.

I found the tweet from Asperi. He posted this screenshot from the Ask Apple event.

In the screenshot, an Apple Engineer state that it is OK to manually initialize a @StateObject variable in an init function.

@StateObject var store: Store

init(id: UUID) {
self._store = StateObject(wrappedValue: Store(id: id))
}

When he got asked about the StateObject documentation, he mentioned that the documentation tries to be more conservative since it might cause confusion and issue to the one that does not get used to the platform.

The above conversation can confirm that it is OK to initialize @StateObject manually, but what about the issue that this can cause?

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

What you should be aware of when manually initializing @StateObject

The only thing that you should be aware of when manually initializing @StateObject is it will initialize once at the beginning of the view lifetime.

The subsequent change of the initialize value wouldn't affect @StateObject.

To get a better understanding of the problem, let's see an example.

We got the DashboardView with a @StateObject.

class DashboardViewModel: ObservableObject {
@Published var greeting: String

init(name: String) {
greeting = "Hello, \(name)!"
}
}

struct DashboardView: View {
@StateObject private var viewModel: DashboardViewModel

// 1
init(name: String) {
_viewModel = StateObject(wrappedValue: DashboardViewModel(name: name))
}

var body: some View {
Text("Greeting: \(viewModel.greeting)")
}
}

1 DashboardView accept the name parameter which use to initialize DashboardViewModel.

We use DashboardView and initialize it with the "Sarunw 2" string, "Sarunw (count)" with the count equals 2.

struct ContentView: View {
// 1
@State var count = 2
var body: some View {
VStack {
// 2
DashboardView(name: "Sarunw \(count)")
Text("Count: \(count)")
Button("+1") {
// 3
count += 1
}
}
}
}

1 The initial value of count is 2.
2 Initialize DashboardView with "Sarunw (count)".
3 We have a button that count value.

DashboardView capture the value of count when it was first created. So the name is "Sarunw 2".

If you later increase the count value, DashboardView wouldn't notice that change.

The StateObject will initialize only once for the lifetime of the DashboardView.

The StateObject will initialize only once for the lifetime of the DashboardView.
The StateObject will initialize only once for the lifetime of the DashboardView.

As you can see, DashboardViewModel take the count value of 2 at the start and no longer change through its lifecycle, even when the count change.


Read more article about SwiftUI, Data, Property Wrapper, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
@State variable initialization in SwiftUI

Learn how to initialize a state variable and discuss whether you should do it or not.

Next
What is the difference between List and ForEach in SwiftUI

Even though ForEach and List have similar syntax, they serve different purposes. Let's learn about their differences.

← Home