1

I've been newly studying SwiftUI. And since I've seen Data flow over SwiftUI video from Apple explaining difference between @ObjectBinding and @EnvironmentObject, a question has come to my mind. What does apple mean by :

You have to pass around the model from hop to hop in @ObjectBinding ? (29':00")

Do we have to pass the object using @binding in another views for using them ?

What if we don't use @binding and reference to it using another @ObjectBinding ?

Does that make an inconvenience or make SwiftUI not to work correctly or views not being sync with each other ?

Parsa Noori
  • 198
  • 1
  • 1
  • 10
  • 1
    There is no more `@ObjectBinding`... it's outdated video. You'd better start from official tutorial [Learn to Make Apps with SwiftUI](https://developer.apple.com/tutorials/swiftui/tutorials) – Asperi Feb 14 '20 at 14:35

1 Answers1

2

[Edit: note that @ObjectBinding is no longer around; instead you can use @State to mark an instance variable as requiring a view refresh when it changes.]

When a view declares an @State or @Binding variable, its value must be explicitly passed from the view's parent. So if you have a long hierarchy of views with some piece of data from the top being used in the bottom, you must code every level of that hierarchy to know about and pass down the data.

In his comment at 29:00, he is contrasting this to using @EnvironmentVariable in a child view, which searches the whole hierarchy for that piece of data. This means any views that do not explicitly need the data can effectively ignore it. Registering a variable needs only be done once (via .environmentObject(_) on a view).

Here is a contrived example. Given some data type conforming to ObservableObject,

class SampleData: ObservableObject {
    @Published var contents: String

    init(_ contents: String) {
        self.contents = contents
    }
}

Consider this view hierarchy:

struct ContentView: View {
    @State private var data = SampleData("sample content")

    var body: some View {
        VStack {
            StateViewA(data: self.data)

            EnvironmentViewA()
                .environmentObject(self.data)
        }
    }
}

struct StateViewA: View {
    @State var data: SampleData

    var body: some View {
        StateViewB(data: self.data)
    }
}

struct StateViewB: View {
    @State var data: SampleData

    var body: some View {
        Text(self.data.contents)
    }
}

struct EnvironmentViewA: View {
    var body: some View {
        EnvironmentViewB()
    }
}

struct EnvironmentViewB: View {
    @EnvironmentObject var data: SampleData

    var body: some View {
        Text(self.data.contents)
    }
}

The result in ContentView will be two views that display the same piece of text. In the first, StateViewA must pass the data on to its child (i.e. the model is passed from "hop to hop"), whereas in the second, EnvironmentViewA does not have to be aware of the data at all.

Ben
  • 425
  • 6
  • 11
  • Thank you for your answer. I got another question : Do you know why @ObjectBinding has been removed from SwiftUI ? And why Apple doesn't mention in WWDC website that the specific materials being taught aren't up to date ? And What is a good source for learning SwiftUI right now which is also up to date ? – Parsa Noori Feb 14 '20 at 16:42
  • 1
    I do not know what all went into that decision, but I have been very pleased with how state and binding work now. Also, great question about why there is no mention of this on the WWDC page; that would be super helpful. If you haven't yet, I would recommend going through [Apple's SwiftUI tutorials](https://developer.apple.com/tutorials/swiftui/tutorials). Also [Hacking with Swift](https://www.hackingwithswift.com/quick-start/swift) has a great introduction as well. – Ben Feb 14 '20 at 18:25
  • I understand that in earlier versions of SwiftUI, the `BindableObject` protocol was part of SwiftUI instead of Combine. Protocol changed to `ObservableObject` with the framework change. – andrewbuilder Jun 12 '20 at 12:20
  • @Ben your answer and contrived example are really helpful and a better explanation than any other I've read or watched, clearly demonstrating the difference between `@State` and `@EnvironmentObject`. *However* perhaps important to note that `@State` is your "single source of truth" and unless I'm mistaken, all subsequent references to that specific wrapped property should be via `@Binding`. So in `struct StateViewA: View` and `struct StateViewB: View`, your wrapped property should be `@Binding var data: SampleData`. Maybe check and if necessary update your answer? – andrewbuilder Jun 12 '20 at 12:30