Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 277
kennyc Avatar asked Jun 22 '19 20:06

kennyc


People also ask

What annotation do we use on a property to set up a binding for SwiftUI?

In SwiftUI, you can create bindings in 2 ways: With the @Binding property wrapper, which creates a binding, but doesn't store it. With other property wrappers, like @State, which creates a binding, and also stores its value.

What is binding string Swift?

String in swift is a value type, so your textValue property is taking a copy of the value, and SwiftUI is monitoring that copy, not the actual value in Controller.message . What you want here is a binding or an observed object—exactly which depends on whether Controller is a struct or a class type.


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

like image 74
malhal Avatar answered Oct 04 '22 03:10

malhal


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")
    }
}
like image 37
Ugo Arangino Avatar answered Oct 04 '22 05:10

Ugo Arangino