2

I simply want to pass two EnvironmentObjects to a view which are different instances of the same class. I feel like I have overlooked something as this seems quite simple.

The most obvious way would be to do it the usual way (which works if they are instances of a different class) like so:

Top-View:

exampleView()
    .environmentObject(vm1)
    .environmentObject(vm2)

Sub-View:

struct exampleView: View {
    
    @EnvironmentObject var vm1: ViewModel
    @EnvironmentObject var vm2: ViewModel

    var body: some View {
        ...
    }
}

But this doesn't work as the view will set both vm1 and vm2 to the original vm1.

I have searched everywhere but could not find any solution except to use a single class and change the properties of the class for example from "property" to "property1" and "property2". This is not only quite ugly code but also not possible in my case.

Why? The project I am working on is quite big and I do not have access to some of the code. The class which is initialised twice owns an array which is filled differently, depending on other factors. In the view, some parts are being reused because the array has the same kind of items, but filled with different content.

If this seems really wrong for Swift UI please tell me. I would have to remake a lot, but on the surface this seemed to be quite a simple problem, so I would appreciate answers where I can keep the structure of my class.

TylerP
  • 9,600
  • 4
  • 39
  • 43
D. Kee
  • 169
  • 14
  • "I feel like I have overlooked something" What you're overlooking might be that an environment object is a singleton. That is the opposite of "different instances of same class". – matt Sep 26 '21 at 15:47
  • Maybe off-topic, but that's not a good way to inject view models, better inject them at initialization time. This will also solve your problem. – Cristik Sep 26 '21 at 15:50
  • @matt could you elaborate? I only know "singleton" as a pattern. Do you mean classes used as Swift's @ EnvironmentObjects are meant to only exist once? – D. Kee Sep 26 '21 at 15:50
  • @Cristik okay, any help like that is much appreciated as I am quite new to Swift and SwiftUI. By injecting at initialization, do you mean like exampleView(vm: vm1, ...)? If so, that is not really possible or would be ugly, since the receiving view is much deeper in the hierarchy, and the viewmodels are initialised in the SceneDelegate – D. Kee Sep 26 '21 at 15:53
  • @D.Kee check the linked question, the answer there should solve your problem. If it doesn't update your question and explain why it's a different issue, and we'll get it reopened. – Cristik Sep 26 '21 at 15:55
  • @Cristik Thanks, but I specifically asked for a different solution, as I already took a look at that post before making the question, I will update my post to explan why it is not possible, or hardly so – D. Kee Sep 26 '21 at 15:58
  • 2
    `EnvironmentObject` works `per-type`, so you can inject only one instance of every specific type as environment object. – Asperi Sep 26 '21 at 17:07
  • View models shouldn't be in the global environment. They should be provided either vis an initialiser to the view, or you can inject them into the environment from the super view of the view that uses them. You typically have a model in the environment and this is used to make the view models as required. This is for exactly the reason you have stated. You need different view models for the same view depending on other state. Injecting the appropriate view model into the environment in the immediate superview may work for you – Paulw11 Sep 26 '21 at 20:34
  • As others have said, you are not using the ViewModel concept the right way. A ViewModel should have an intimate relation with the View it provides data for. And the fact that you build the ViewModels in a completely unrelated part of the code is a code smell. How are those view model used in your app? Can you replace them by simple data objects, and construct the actual view model based on those data objects? – Cristik Sep 27 '21 at 14:24

0 Answers0