Initialize @StateObject with a parameter in SwiftUI

I would like to know if there is currently (at the time of asking, the first Xcode 12.0 Beta) a way to initialize a @StateObject with a parameter coming from an initializer.

To be more specific, this snippet of code works fine:

struct MyView: View {
  @StateObject var myObject = MyObject(id: 1)
}

But this does not:

struct MyView: View {
  @StateObject var myObject: MyObject

  init(id: Int) {
    self.myObject = MyObject(id: id)
  }
}

From what I understand the role of @StateObject is to make the view the owner of the object. The current workaround I use is to pass the already initialized MyObject instance like this:

struct MyView: View {
  @ObservedObject var myObject: MyObject

  init(myObject: MyObject) {
    self.myObject = myObject
  }
}

But now, as far as I understand, the view that created the object owns it, while this view does not.

Thanks.


Solution 1:

The answer given by @Asperi should be avoided Apple says so in their documentation for 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.

Apple tries to optimize a lot under the hood, don't fight the system.

Just create an ObservableObject with a Published value for the parameter you wanted to use in the first place. Then use the .onAppear() to set it's value and SwiftUI will do the rest.

Code:

class SampleObject: ObservableObject {
    @Published var id: Int = 0
}

struct MainView: View {
    @StateObject private var sampleObject = SampleObject()
    
    var body: some View {
        Text("Identifier: \(sampleObject.id)")
            .onAppear() {
                sampleObject.id = 9000
            }
    }
}

Solution 2:

Here is a demo of solution. Tested with Xcode 12b.

class MyObject: ObservableObject {
    @Published var id: Int
    init(id: Int) {
        self.id = id
    }
}

struct MyView: View {
    @StateObject private var object: MyObject
    init(id: Int = 1) {
        _object = StateObject(wrappedValue: MyObject(id: id))
    }

    var body: some View {
        Text("Test: \(object.id)")
    }
}