seth.eeee
seth.eeee

Reputation: 519

How do I initialize a View which takes in a StateObject as a parameter?

struct ProfileEditView: View {

    @ObservedObject var viewModel: UsersViewModel
    @StateObject var auth: Authenticator

    @State var showingImageEditor: Bool = false

    init(_ viewModel: UsersViewModel, _ auth: Authenticator) {
    
        self.viewModel = viewModel
        self.auth = auth
    
        UITableView.appearance().backgroundColor = UIColor.clear
        UITableViewCell.appearance().selectionStyle = .none
    }

    var body: some View { }
}

I am trying to manually intialize a View that receives a StateObject as a parameter. I am getting the following error:

Cannot assign to property: 'auth' is a get-only property

What is the proper way to write the initializer?

Upvotes: 26

Views: 11089

Answers (2)

OhadM
OhadM

Reputation: 4803

In addition to the 'accepted answer' there's a really good example that will allow everyone to reproduce the error and understand how to avoid it.

Considering there's a viewModel in a View with a property wrapper @StateObject, there's no need to initiate a StateObject(wrappedValue: ...) in the constructor as long as there are no private properties in the View. Let's see an example:

class MyViewModel: ObservableObject {}

struct MyView: View {
   @StateObject var viewModel: MyViewModel
}

The above example will work by simply using:

MyView(viewModel: MyViewModel())

But if we will add a private property to the View:

struct MyView: View {
   @StateObject var viewModel: MyViewModel
   // `test` will produce the protection level error
   private var test = false
}

Our previous example MyView(viewModel: MyViewModel()) will now produce the following error:

initializer is inaccessible due to 'private' protection level

So please consider why you declare a property in a View private, perhaps it can be moved to the ViewModel and the issue will be solved in order to utilize the dependency injection principle.

Upvotes: 0

Mozahler
Mozahler

Reputation: 5303

I can't fully reproduce your code without your definitions of Authenticator and UsersViewModel, but I got it to compile:

class UsersViewModel: ObservableObject {}
class Authenticator: ObservableObject {}

struct ProfileEditView: View {
    @ObservedObject var viewModel: UsersViewModel
    @StateObject var auth: Authenticator

    @State var showingImageEditor: Bool = false

    init(_ viewModel: ObservedObject<UsersViewModel>, _ auth: Authenticator) {

        _viewModel = viewModel
        _auth = StateObject(wrappedValue: auth)

        UITableView.appearance().backgroundColor = UIColor.clear
        UITableViewCell.appearance().selectionStyle = .none
    }

    var body: some View {
        Text("something")
    }
}

These are the key changes:

init(_ viewModel: ObservedObject<UsersViewModel>, _ auth: Authenticator) {
    _viewModel = viewModel
    _auth = StateObject(wrappedValue: auth)

If you don't understand my changes you should google

"swift property wrappers"

to get a better understanding of what property wrappers are and how to use them.

Upvotes: 50

Related Questions