Combine framework is a new framework that Apple unveiled in this year’s WWDC. Combine helps developers work with synchronous and asynchronous tasks. Let’s take the example of a “Sign Up” form.

In such a form, we have a username field and two password fields (for verification) and a “Create Account” button. The username needs to check from the servers to see if it is valid, or not already taken. The password fields need to be checked locally to see if the password in both fields matches or not. Once all these checks have taken place, only then should the button be enabled.

combine framework

This form of a situation requires asynchronous and synchronous verification. Only if the result we get from via network and locally are favorable, that we proceed to the next task – which enables the button.

Combine helps us in this by giving us three main ingredients.

  • Publishers
  • Subscribers
  • Operators

Publishers can be thought of as Observables. The publishers are basically values that can be changed and the subscribers(observers) pick up on these changes and are updated. Usually, publishers send a value that is not the correct type. Operators are used as the middle-man between the publisher and subscriber. It converts the value to the correct type.

We will keep this tutorial really simple and just talk about localized publishers and subscribers.

Let’s say when we press a button – we create a new model object. When this new object is created, we update the label saying that the new object has been created. This is a pretty trivial example just to show how publishers, subscribers, and operators work in tandem.

First, let’s create a model that will have a title and description:

struct NewPost {
    var title : String
    var description: String
}

Then we create a new notification name called “new_Post_Object”. When a new post will be created we will broadcast a notification but at the same time emit elements that will be used by our subscriber.

Now let’s create a publisher:

let newPostPublisher = NotificationCenter.Publisher(center: .default, name: .newPostObject, object: nil)
            .map { (notification) -> String? in //map is an operator
                return (notification.object as? NewPost)?.title ?? ""
}

newPostPublisher is a publisher that will emit elements when the specific notification is broadcasted. That emitted element is of type Notification. However, we would like to know the object that has been created. So we use the .map operator.

There are tons of operators and each has limited functionality. You can chain these operators to get the correct return type. Map operator will grab the notification type element and cast its object as NewPost and return the optional string value.

So our publisher returns an optional String with no error. 

Now let’s move to our subscriber. The publisher has exposed the change to the environment, now it’s up to the subscriber to act on it.

let newPostSubscriber = Subscribers.Assign(object: newPostCreatedLabel, keyPath: \.text)
newPostPublisher.subscribe(newPostSubscriber)

We create a subscriber and assign the change to a UILabel’s text property. Then we subscribe the publisher to the subscriber and voila!

Now whenever a new post is created, the notification is posted. The published value is of type Notification but we need String, so we use operator (.map) to convert the Notification.object to Post object and get the optional String value.

The changed value is then exposed and the subscriber picks on it and updates the label.

let newPost = NewPost(title: "New Combine Post", description: "This post is an introduction to Combine.")
NotificationCenter.default.post(name: .newPostObject, object: newPost)

@Published annotation behind any variable makes it in to a publisher. For example:

@Published var isAllowed: Bool = false

isAllowed is now a publisher and its value is exposed to which the subscriber can subscribe to and update itself.

In order to do that, we use the dollar sign in front of it.

$isAllowed.receive(on: DispatchQueue.main).assign(to: \.isEnabled, on: someButton)

The isAllowed variable can be changed using a UISwitch or a button press. On change, we connect the subscriber to that publisher (hence we use the $ sign – to show that we are using the variable as a Publisher) and make sure that the changes are received on the main thread. The assign function assigns it’s updated value to someButton‘s isEnabled property.

Receive modifier is quite important here. If this publisher was to be sending info downstream to a subscriber from an asynchronous task we needed to make sure that the UI change took place on the main thread. This rectifies the need to explicitly call the assign method inside the main thread. This does it for us.

 


Leave a Reply

Your email address will not be published. Required fields are marked *