Best data-binding practice in Combine + SwiftUI? : <p>In RxSwift it's pretty easy to bind a <code>Driver</code> or an <code>Observable</code> in a <code>View Model</code> to some observer in a <code>ViewController</code> (i.e. a <code>UILabel</code>).</p>

<p>I usually prefer to build a pipeline, with observables <strong>created from other observables</strong>, instead of "imperatively" pushing values, say via a <code>PublishSubject</code>).</p>

<p>Let's use this example: <strong>update a <code>UILabel</code> after fetching some data from the network</strong></p>

<hr>

<h2>RxSwift + RxCocoa example</h2>

<pre class="lang-swift prettyprint-override"><code>final class RxViewModel {
    private var dataObservable: Observable&lt;Data&gt;

    let stringDriver: Driver&lt;String&gt;

    init() {
        let request = URLRequest(url: URL(string:"https://www.google.com")!)

        self.dataObservable = URLSession.shared
            .rx.data(request: request).asObservable()

        self.stringDriver = dataObservable
            .asDriver(onErrorJustReturn: Data())
            .map { _ in return "Network data received!" }
    }
}
</code></pre>

<pre class="lang-swift prettyprint-override"><code>final class RxViewController: UIViewController {
    private let disposeBag = DisposeBag()
    let rxViewModel = RxViewModel()

    @IBOutlet weak var rxLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        rxViewModel.stringDriver.drive(rxLabel.rx.text).disposed(by: disposeBag)
    }
}
</code></pre>

<hr>

<h2>Combine + UIKit example</h2>

<p>In a UIKit-based project it seems like you can keep the same pattern:</p>

<ul>
<li>view model exposes publishers</li>
<li>view controller binds its UI elements to those publishers</li>
</ul>

<pre class="lang-swift prettyprint-override"><code>final class CombineViewModel: ObservableObject {
    private var dataPublisher: AnyPublisher&lt;URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure&gt;
    var stringPublisher: AnyPublisher&lt;String, Never&gt;

    init() {
        self.dataPublisher = URLSession.shared
            .dataTaskPublisher(for: URL(string: "https://www.google.it")!)
            .eraseToAnyPublisher()

        self.stringPublisher = dataPublisher
            .map { (_, _) in return "Network data received!" }
            .replaceError(with: "Oh no, error!")
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}
</code></pre>

<pre class="lang-swift prettyprint-override"><code>final class CombineViewController: UIViewController {
    private var cancellableBag = Set&lt;AnyCancellable&gt;()
    let combineViewModel = CombineViewModel()

    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        combineViewModel.stringPublisher
            .flatMap { Just($0) }
            .assign(to: \.text, on: self.label)
            .store(in: &amp;cancellableBag)
    }
}
</code></pre>

<hr>

<h2>What about SwiftUI?</h2>

<p>SwiftUI relies on property wrappers like <code>@Published</code> and protocols like <code>ObservableObject</code>, <code>ObservedObject</code> to automagically take care of bindings (As of <em>Xcode 11b7</em>).</p>

<p>Since (AFAIK) property wrappers cannot be "created on the fly", there's no way you can re-create the example above using to the same pattern.
The following <strong>does not compile</strong></p>

<pre class="lang-swift prettyprint-override"><code>final class WrongViewModel: ObservableObject {
    private var dataPublisher: AnyPublisher&lt;URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure&gt;
    @Published var stringValue: String

    init() {
        self.dataPublisher = URLSession.shared
            .dataTaskPublisher(for: URL(string: "https://www.google.it")!)
            .eraseToAnyPublisher()

        self.stringValue = dataPublisher.map { ... }. ??? &lt;--- WRONG!
    }
}
</code></pre>

<p>The closest I could come up with is <strong>subscribing in your view model (UGH!)</strong> and <strong>imperatively update your property</strong>, which does not feel right and reactive at all.</p>

<pre class="lang-swift prettyprint-override"><code>final class SwiftUIViewModel: ObservableObject {
    private var cancellableBag = Set&lt;AnyCancellable&gt;()
    private var dataPublisher: AnyPublisher&lt;URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure&gt;

    @Published var stringValue: String = ""

    init() {
        self.dataPublisher = URLSession.shared
            .dataTaskPublisher(for: URL(string: "https://www.google.it")!)
            .eraseToAnyPublisher()

        dataPublisher
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: {_ in }) { (_, _) in
            self.stringValue = "Network data received!"
        }.store(in: &amp;cancellableBag)
    }
}
</code></pre>

<pre class="lang-swift prettyprint-override"><code>struct ContentView: View {
    @ObservedObject var viewModel = SwiftUIViewModel()

    var body: some View {
        Text(viewModel.stringValue)
    }
}
</code></pre>

<p>Is the "old way of doing bindings" to be forgotten and replaced, in this new <strong>UIViewController-less</strong> world?</p>
