25

I have been reading about the property wrappers in SwiftUI and I see that they do a great job, but one thing which I really don't get is the difference between @EnvironmentObject and @ObservedObject.

From what I learned so far, I see that @EnvironmentObject is used when we have an object that is needed in various places in our app but we don't need to pass it through all of them. For example if we have hierarchy A -> B -> C -> D and the object is created at A, it is saved in the environment so that we can pass it directly from A to D, if D needs it.

If we use @ObservedObject which is created at A and needs to be passed to D, then we need to go through B and C as well.

But I still don't know how to decide which one to use. Here are 2 example projects which I made:

struct ContentView2: View {
 
   var order = Order2()

   var body: some View {
      VStack {
           EditView2()
           DisplayView2()
       }
       .environmentObject(order)
   }
}
struct EditView2: View {
   @EnvironmentObject var user: Order2
 
   var body: some View {
       HStack{
       TextField("Fruit", text: $user.item)
       }
   }
}
struct DisplayView2: View {
   @EnvironmentObject var user: Order2
   var body: some View {
       VStack{
       Text(user.item)
       }
   }
}
class Order2: ObservableObject {
       @Published var item = "Orange"
   }

and

struct ContentView: View {

    var order = Order()
    
    var body: some View {
       VStack {
            EditView(order: order)
            DisplayView(order: order)
        }
    }
}
struct EditView: View {
    @ObservedObject var order: Order
    var body: some View {
        HStack{
        TextField("Fruit", text: $order.item)
        }
    }
}
struct DisplayView: View {
      @ObservedObject var order: Order
      var body: some View {
        VStack{
        Text(order.item)
        }
    }
}
class Order: ObservableObject {
    @Published var item = "Apple"
}

Both codes do the same update of the view. Also both ContentViews, pass an Order object. The difference is that Environment passes .environmentObject(order) and Observed passes it directly EditView(order: order). For me, both do same job, only their declaration is different, therefore I would appreciate some explanation or a better example.

Dakata
  • 1,227
  • 2
  • 14
  • 33
  • 4
    The difference is only in injection... ObservedObject you must inject like any other property - at each init, but environment object is injected only *once* at root of view hierarchy... and available for any far deep view. That's it. – Asperi Aug 10 '20 at 16:11

1 Answers1

25

As you've noticed an @ObservedObject needs to be passed from view to view. It may be better for a simple view hierarchy when you don't have too many views.


Let's assume you have the following hierarchy:

ViewA -> ViewB -> ViewC -> ViewD

Now if you want your @ObservedObject from the ViewA to be in the ViewB there's no problem with passing it directly in init.

But what if you want it in the ViewD as well? And what if you don't need it in the ViewB and ViewC?

With an @ObservedObject you'd need to manually pass it from the ViewA to the ViewB and then to the ViewC, and then to the ViewD. And you'd need to declare it in every child view.

With an @EnvironmentObject it's easy - just pass it to the top-level view:

ViewA().environmentObject(someObservableObject)

Then you only declare it in the view that uses it - this may make your code more readable.


Note

Every object in the environment (view hierarchy) can access the injected @EnvironmentObject. If you don't want this (privacy is important) you may need to pass it as an @ObservedObject instead.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • 1
    Thank you very much for the detailed description. To summarize it, when I declare @EnvironmentObject at ViewA all child views will have access to it. Does it work also for sheet views? For example, I have a .sheet presented from ViewA – Dakata Aug 10 '20 at 16:22
  • 3
    @Dakata No, unfortunately you need to inject it again to your sheet. But only once when you call it. Then all child views in the sheet will have access to your EnvironmentObject. Think of it like a sheet has its own environment. – pawello2222 Aug 10 '20 at 16:24
  • 2
    Note that you can use private types as EnvironmentObject which stops scopes that don't know about that type from accessing it – Casper Zandbergen Jan 18 '21 at 10:47
  • 2
    This is the best explanation, I understood it fully. Thank you! – Binaya Thapa Magar Sep 24 '21 at 09:32
  • 1
    Further to previous comments, EnvironmentObject works with sheets too. – Kramer Jan 23 '23 at 13:47