DenFav
DenFav

Reputation: 2811

Passing a BindableObject to the views

From the Apple's sessions and Tutorial we have two options to pass BindableObject to the views.

  1. Use declare BindableObject as a Source of truth in the top view in the hierarchy with @ObjectBinding wrapper and pass it to other views with @Binding declared.
  2. Use declare BindableObject as a Source of truth in the top view in the hierarchy with @EnviromentObject wrapper, init top view with .enviroment(BindableObject) modifier and pass it to other views or with @Binding declared, or using @EnviromentObject(in this case BindableObject will be assigned automatically by SwiftUI and we do not need to pass it on init).

From Handling User Input tutorial if we have BindableObject with list of items in and we want to change one of the items on other view(or RowView or even separate screen) we need to:

  1. Pass the BindableObject to the deeper view using any of the ways above.
    1. Pass the selected item to this view.
    2. Bind the property of the item with BindingView by finding the item in BindableObject list.

Some code to make the question clear:

Message model and BindableObject

struct Message: Identifiable {
    var id: String
    var toggle: Bool = true
}

class MessageStore: BindableObject {

  let didChange = PassthroughSubject<MessageStore, Never>()

  var messagesList: [Message] = testData {
    didSet {
      didChange.send(self)
    }
  }
}

MessageView that represents a list of items struct MessagesView

 : View {

    @EnvironmentObject var messageStore: MessageStore

    var body: some View {
        NavigationView {
            List(messageStore.messagesList) { message in
                NavigationButton(destination: Text(message.id)) {
                    MessageRow(message: message)
                }
            }
            .navigationBarTitle(Text("Messages"))
        }
    }
}

MessageRow that has a Toggle to update the state of particular item in out BindableObject

struct MessageRow: View {

    @EnvironmentObject var tags: MessageStore
    var message: Message

    var messageIndex: Int {
        tags.messagesList.firstIndex { $0.id == message.id }!
    }


    var body: some View {
       Toggle(isOn: self.$tags.messagesList[self.messageIndex].toggle) {
           Text("Test toogle")
       }
    }

}

This approach is shown in the tutorial I mentioned above.

Question: I wanted to pass Message separately as a @Binding to work with it in the child view directly, but I wasn't able to implement this. I became a bit confused. Is it proper way to pass to any view(that should handle bindings) both the BindableObject and selected item to bind later the item from BindableObject using index? Is there any other way that will allow to pass not a full BindableObject but a part of it and bind this part(it should be Source of truth), in our case this part is Message?

Upvotes: 2

Views: 889

Answers (1)

Miras Karazhigitov
Miras Karazhigitov

Reputation: 7

I think you are overcomplicating it. It seems that all you need is a toggle value in your message. Try this:

var body: some View {
    NavigationView {
        List(messageStore.messagesList) { message in
            NavigationButton(destination: Text(message.id)) {
                MessageRow(isTogged: $message.toggle)
            }
        }
        .navigationBarTitle(Text("Messages"))
    }
}

struct MessageRow: View {

    @Binding var isToggled: Bool

    var body: some View {
        Toggle(isOn: self.isToggled) {
            Text("Test toogle")
        }
    }
}

Upvotes: 0

Related Questions