Can you use a Publisher directly as an @ObjectBinding property in SwiftUI?

Viewed 9930

In SwiftUI, can you use an instance of a Publisher directly as an @ObjectBinding property or do you have to wrap it in a class that implements BindableObject?

let subject = PassthroughSubject<Void, Never>()
let view = ContentView(data:subject)

struct ContentView : View {
  @ObjectBinding var data:AnyPublisher<Void, Never>
}

// When I want to refresh the view, I can just call: 
subject.send(())

This doesn't compile for me and just hangs Xcode 11 Beta 2. But should you even be allowed to do this?

2 Answers

In your View body use .onReceive passing in the publisher like the example below, taken from Data Flow Through SwiftUI - WWDC 2019 @ 21:23. Inside the closure you update an @State var, which in turn is referenced somewhere else in the body which causes body to be called when it is changed.

enter image description here

You can implement a BindableObject wich takes a publisher as initializer parameter.

And extend Publisher with a convenience function to create this BindableObject.

class BindableObjectPublisher<PublisherType: Publisher>: BindableObject where PublisherType.Failure == Never {

    typealias Data = PublisherType.Output

    var didChange: PublisherType
    var data: Data?

    init(didChange: PublisherType) {
        self.didChange = didChange
        _ = didChange.sink { (value) in
            self.data = value
        }
    }
}

extension Publisher where Failure == Never {
    func bindableObject() -> BindableObjectPublisher<Self> {
        return BindableObjectPublisher(didChange: self)
    }
}

struct ContentView : View {
    @ObjectBinding var binding = Publishers.Just("test").bindableObject()

    var body: some View {
        Text(binding.data ?? "Empty")
    }
}
Related