hmlongco / Resolver

Swift Ultralight Dependency Injection / Service Locator framework
MIT License
2.14k stars 187 forks source link

Using Args in SwiftUI #137

Closed saltyskip closed 2 years ago

saltyskip commented 2 years ago

Currently my project follows an MVVM structure with Swiftui, and I would like to migrate my @observableobjects to resolver objects, but what I'm gathering after reading the documentation and this issue

https://github.com/hmlongco/Resolver/issues/115

it seems that its not possible to pass args to my view models. Additionally the workaround in the documentation recommends using the uikit viewdidload lifecycle event in order to pass args to these lazy view models.

Is there a suitable workaround for using args in resolver for swiftUI?

hmlongco commented 2 years ago

So... it's SwiftUI. What would you do if you were using pure SwiftUI and using @StateObject to hold your view model?

How would you pass a parameter to it?

The following is a classic SwiftUI pattern...

enum LoadingState<Value> {
    case initial
    case loading
    case loaded(Value)
    case empty(String)
    case error(String)
}

class LoadingViewModel: ObservableObject {
    @Published private(set) var state: LoadingState<String> = .initial
    func load(parameter: String) {
        state = .loaded("The view is loaded for \(parameter).")
    }
}

struct LoadingView: View {
    let parameter: String
    @StateObject var viewModel = LoadingViewModel()
    var body: some View {
        switch viewModel.state {
            case .initial, .loading:
                ProgressView()
                    .onAppear {
                        viewModel.load(parameter: parameter)
                    }
            case .loaded(let value):
                Text(value)
            case .empty(let message):
                Text(message)
                    .foregroundColor(.gray)
            case .error(let message):
                Text(message)
                    .foregroundColor(.red)
        }
    }
}

The parameter is passed to the view, which will show the progress view, which, on appearance will pass the parameter from the view to the load method of the view model.

The same would apply if I were using @InjectedObject instead of @StateObject.

You might take a look at the following article of mine in regard to view model composition... https://betterprogramming.pub/swiftui-view-models-are-not-protocols-8c415c0325b1

And maybe even this one, just to make sure you have a handle on SwiftUI object lifecycles... https://medium.com/swlh/deep-inside-views-state-and-performance-in-swiftui-d23a3a44b79