Swift — @propertyWrapper with Publisher

Jullian Mercier
2 min readMar 21, 2020

--

Property wrappers are one of Swift’s major new features enabling us to attach some specific behavior each time a property gets accessed or mutated.

It comes in very handy when one needs to encapsulate some common logic such as the user defaults which are often read and written to throughout the app lifecycle.

If you’ve been playing with SwiftUI, you may have already encountered some of these wrappers: @EnvironmentObject , @ObservableObject, or @Published.

Declaring a property wrapper is pretty straightforward since it only requires us to implement a `wrappedValue` property.

Let’s build a property wrapper that encapsulates some reactive behavior in which any passed-in value is immediately sent to a subject which is bound to a publisher in order for a stream of these values to be observed.

  1. Implement the wrapped value
  2. Send the new value to a subject
  3. Type-erase the subject with a publisher

We create our property wrapper (marked with the @ attribute), implement the required `wrappedValue` property and provide with an init() method using a generic `T` type.

@propertyWrapper
class PublisherConvertible<T> {
var wrappedValue: T
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}

First setup is done.

Then, create a `CurrentValueSubject` with the `wrappedValue` as initial value.

A subject enables us to bring imperative code into the Combine framework.

Every time a value is set, it is sent over to the subject (which is marked as private since it should not be accessible from outside the wrapper).

@propertyWrapper
struct PublisherConvertible<T> {
var wrappedValue: T {
willSet {
subject.send(newValue)
}
}
private lazy var subject = CurrentValueSubject<T, Error>(wrappedValue)
}

So far so good however there’s one piece missing which is the publisher from which we’ll be observing the stream of values.

We could use a regular computed property however a property wrapper also has a built-in `projectedValue` which is quite useful here.

@propertyWrapper
class PublisherConvertible<T> {
var wrappedValue: T {
willSet {
subject.send(newValue)
}
}
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
private lazy var subject = CurrentValueSubject<T, Error>(wrappedValue) var projectedValue: AnyPublisher<T, Error> {
return subject.eraseToAnyPublisher()
}
}

Finally, let’s declare a variable using our newly created property wrapper.

@PublisherConvertible var name = "John"$name
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
.store(in: &subscriptions)
name = "Jack"
name = "Joe"
// John
// Jack
// Joe

The $ syntax represents the `projectedValue` property we just created, there is another way to access it using `_name.projectedValue`.

The @Published attribute, part of the Foundation framework, is actually doing what we implemented through our `PublisherConvertible` wrapper but it is nice to see how things are done under the hood in order to fully explore the potential of property wrappers.

Thanks for reading, follow me on Twitter for my latest articles @jullian_mercier

--

--

Jullian Mercier

Senior iOS engineer. jullianmercier.com. @jullian_mercier. Currently looking for new job opportunities.