SwiftUI’s tight integration with Combine means that the publisher-subscriber paradigm is presented in a different way from what you’re used to from previous examples. In fact, the paradigm is almost entirely hidden from you, only exposing some hooks via property wrappers and interfaces.
Decorating a property of a SwiftUI view with the @State property wrapper sets up a Combine-based connection behind the scenes between the view and that property. When that property changes, the new value is published, and the View - acting as the subscriber in this case - is asked by the rendering system to redraw itself. Bindings are passed into SwiftUI views to allow the view to change the property’s value.
But what if you have an object that is outside a SwiftUI view? @State and Binding won’t work here - they deal with properties within a SwiftUI view.
Luckily, the @Published property wrapper exits for just such a case. @Published properties get publishers synthesized for them behind the scenes, which you can access via the $ prefix — e.g., $fetching, just like with a Binding. Let’s go over to the code and add the property wrappers.
To enable published values in our model, go to the ChuckNorrisJokesModel target, and open the View Model folder and select the JokesViewModel.swift file. Below the line that creates the decoder, define the following state properties, decorating them with an @Published property wrapper:
@Published public var fetching: Bool = false
@Published public var joke: Joke = Joke.starter
@Published public var backgroundColor = Color("Gray")
@Published public var decisionState: DecisionState = .undecided
@Published public var showTranslation = false
If you were to run the app now, you wouldn’t see the app respond to any changes to these properties, since nothing subscribes to these publishers yet; we’ll revisit this later when we hook up the view to the view model.