How to mutate a struct variable in init

Given the following example:

public struct Driver {
    @StateObject var client: Client
    
    init(_ url: URL) {
        self.client = Client(url)
    }
}

public class Client: ObservableObject {
    var url: URL
    
    init(_ url: URL) {
        self.url = url
    }
}

This is just a minimum example. In this case the attempt to set client in the Driver init is resulting in Cannot assign to property: 'client' is a get-only property. I thought that @StateObject would allow me to change the properties value?

The reason you get this error is because the wrappedValue is get-only, so you'd need to initialise the backing property directly to set the value i.e. self._client = .init(wrappedValue: Client(url)), but it would be better if you don't do that as you're not supposed to call this initializer on StateObject. You will need to provide a default value inline and then you can mutate it.

I would recommend adding a method on Client like func updateURL(to:) and then you can call it somewhere once you have the url value, for example:

public struct Driver: View {
  @StateObject var client = Client()
    
  var body: some View {
    Text("Hello world")
      .onAppear() {
        client.updateURL(to: url)
      }
    }
  }
}
2 Likes

This looks to me like a property wrapper bug. Property wrappers do allow the backing storage to be initialized via wrapped value in an init, but there seems to be a bug that prevents this from working when init(wrappedValue:) takes an @autoclosure, which StateObject's initializer does.

4 Likes

This is false. The wrappedValue is getable and setable. See the documentation.

It isn't for @StateObject.

3 Likes

I don't think we can obey the "avoid _" doctrine, because the

feature isn't backed by a syntactical feature that would allow for use of initializers that take more arguments than just wrappedValue.

Oh, my bad. I misread the comment.

Terms of Service

Privacy Policy

Cookie Policy