iosswiftmvvmviewmodel

How to pass a string to a view model upon initialization?


I have an iOS app with the following views:

struct LoggedInUserView: View{
    @ObservedObject var viewModel: LoggedInUserViewModel
    @StateObject var sharedPortionViewModel: SharedPortionViewModel(userName: "")
    var body: some View{
        //Some specific-to-LoggedInUser UI elements
        SharedPortionUserView(sharedPortionViewModel: sharedPortionViewModel)
    }
}

struct OtherUserView: View{
    @ObservedObject var viewModel: OtherUserViewModel()
    @StateObject var sharedPortionViewModel: SharedPortionViewModel(userName: "")
    var body: some View{
        //Some specific-to-OtherUser UI elements
        SharedPortionUserView(sharedPortionViewModel: sharedPortionViewModel)
    }
}

SharedPortionViewModel runs the same functions to load data in both cases, but it grabs the specified user's info, depending on which view is being viewed. 100% of the time LoggedInUserView is viewed, the logged-in-user's userName will be passed. This is stored in Keychain.

All I am trying to do is pass the relevant user's userName to the creation of the StateObject in both cases, but I am finding this surprisingly complex to do. A variety of approaches I have seen involve custom initializers in the view, which I'd like to avoid. I also considered something like:

var passedUserName: String //tried a variety of different property wrappers
@StateObject var sharedPortionViewModel: SharedPortionViewModel(userName: passedUserName)

Such that the parent view can pass the userName in question when loading this view, but I think there are issues with passedUserName not being set at initialization to be ready to pass to sharedPortionViewModel.

I also considered modifying the above approach by:

@State private var sharedProfileViewModel: SharedUserProfilePageViewModel?

And then initializing it via

.onAppear {
            if sharedProfileViewModel == nil {
                sharedProfileViewModel = SharedUserProfilePageViewModel(userName: userName)
            }
        }

But this just feels weird.

I could create the viewModel before initializing the navigation from the parent view, and then pass this as the already-created-viewmodel, but this feels clunky.

Am I missing something? Why is this so involved? Are these really the only ways to achieve this? Did I cover the best approach above and improperly dismissed it?


Solution

  • You may implicitly initialize @StateObject like this:

    struct OtherUserView: View {
        @StateObject private var sharedPortionViewModel: SharedPortionViewModel
    
        init(passedUserName: String) {
            self._sharedPortionViewModel = .init(wrappedValue: .init(userName: passedUserName))
        }
    }