Article in iOS App Development category.

Conquering ReactiveSwift: Primitives

Primitives are a very important part of ReacetiveSwift. In this post we will explain exactly how you can use them.

We at Fueled use ReactiveSwift in much of our iOS development. In Part One of our Conquering ReactiveSwift Series, we analyzed the difference between the imperative way and the functional reactive way of solving a problem. For Part Two, we will dive into the realm of ReactiveSwift, starting with the various primitives(basic building blocks) of ReactiveSwift.

Let’s get started

The creators of ReactiveSwift had the following intentions in mind:

ReactiveSwift offers composable, declarative and flexible primitives that are built around the grand concept of streams of values over time.

ReactiveSwift comes with many powerful primitives which enables you to write code in a functional reactive way. The easiest way to learn these primitives is by grouping them into different categories according to their roles. Please refer to the table below.

Source

In reactive programming, we model our system against a series of changes over time, rather than against a state at a particular point in time. The primitives under the “Source” category, are responsible for the generation and propagation of those changes. We have the following primitives under this category: Event, Signal, Signal Producer, Action.

1. Event

An Event denotes something that happened. It is the basic transfer unit of a stream. There are four types of events:

  • Value: An event with valid information
  • Failed: An event denoting error
  • Completed: An event denoting end the stream. No more event can be emitted after this event.
  • Interrupted: Denotes event production is interrupted.

2. Signal

A Signal is a unidirectional stream of events. One can access each event of a signal by subscribing via observers. Observing a signal does not inject any side effects to the signal. All the events are sent simultaneously to all the observers of a given signal.

3. SignalProducer

As the name suggests, a SignalProducer is something that produces a signal. It encapsulates the work which can be started later (and can be repeated too). In other words, a SignalProducer producer encapsulates a deferred and repeatable work. When it is started, a new signal is created, which emits values as the result of an invocation of the encapsulated work.  A SignalProducer is said to be cold as it needs to be started, whereas a Signal is warm, which doesn’t need to be started.

4. Action

Action too is used in the context of deferred and repeatable work. It encapsulates the work of a SignalProducer. We can exercise better control over an Action rather than a Signal Producer. For example, we can control the output by sending different input values. An Action can be enabled or disabled and this state can be controlled reactively through a Property(discussed below). When being invoked with an input, an Action applies the input and the latest state to the predefined work, and pushes the output to the observers.

5. Property

A Property is an observable box which stores a value and notifies observers about future changes to that value. It has getters for producer and signal which emit its values that can be observed. A Property can be initialized either with an initial value and a Signal, a SignalProducer or directly via another Property.

The following image illustrates how the various primitives are related to each other.

Consumer

The primitives under this category listen and act upon the events emitted by Source primitives. There are two primitives under the Consumer category:

  1. Observer
  2. MutableProperty

1. Observer

An Observer encapsulates the work to be performed in response to the events emitted. It is a simple wrapper around a closure which takes Event as an input.

2. MutableProperty

A MutableProperty is an observable box which stores a value just like a Property. It also has getters for producer and signal which emit its values that can be observed. However, unlike a Property, it can be mutated directly. It conforms to the protocol BindingTargetProvider, so it can update its value with the values received by either by a Signal, a SignalProducer, or a Property through <~ (the binding operator)

Operators

Operators specify the way the source is being consumed by the consumer. ReactiveSwift comes with a rich set of operator for performing side effects, transformation, combine, flatten etc. These will be discussed in subsequent articles.

Scope

Primitives under this category determine the lifetime of the interaction between Source and Consumer. There are two primitives under the Scope category:

  1. Disposable
  2. Lifetime

1. Disposable

A Disposable is a mechanism for memory management and cancellation. Observing a Signal returns a disposable. Disposing the disposable prevents the observer from receiving any future events from that signal. However, this does not affect the signal. On starting a SignalProducer, we get a disposable which can be used to cancel the work that has been started.

2. Lifetime

Lifetime represents the lifetime of an object. This primitive is useful when an observation might outlive the observer. For example, we want to observe a notification as long as a UI component is on the screen.

Putting the pieces together

Now let’s understand how to use these primitives to get our job done. Let’s take an example. Suppose we have a signal which emits values of type String?. We need to enable a button when the character count of the string emitted by the signal is greater than 10.

Note: For the time being, let’s not dive into the detail of how we create a signal. This will be discussed in the next article.

Step 1: Defining a signal

let signal: Signal<String?, NoError>

Step 2: Transforming a signal

The signal, we get in the first step emits an optional string. First, we need to transform the nil string to an empty string via map operator. Next, we have to transform it again to emit boolean values.Here we have used skipNil to ignore nil strings.

let transformedSignal = signal
.map { text in
 text ?? ""
}.map { text in
    text.characters.count > 10

}

Step 3: Observing Signal

Now we will observe the transformed signal and perform action, i.e setting isEnabled on the button. Here, we will create an observer of type <Bool, NoError> which means it can consume the signal/signal producer of type <Bool, NoError>. Then we start observing the transformed signal l via observe primitive.

let observer =
Signal<Bool, NoError>.Observer(value: { value in
   button.isEnabled = value
})

let disposable = transformedSignal.observe(observer)

Step 4: Stop observing a signal

Observe primitive returns a Disposable instance, which can be used to stop observing a signal.

disposable.dispose()

It is a best practice to dispose all the observers in the deinit of a class. In the scope of the article, disposable is non-optional.

The whole thing…….

//Defining consumer
let observer =
Signal<Bool, NoError>.Observer(value: { value in
  button.isEnabled = value
})

//Defining source
let transformedSignal = signal
.map{ text in
 text ?? ""
}.map { text in
    text.characters.count > 0
}

//Consuming signal
let disposable = transformedSignal.observe(observer)

//Limit Scope
disposable.dispose()

Conclusion

In this article, we covered different primitives of ReactiveSwift and how we can use them to write code in functional reactive way.

You can find sample code here. In the next article, we will discuss how to create and observe a signal.

More Articles By susmitahorrow

Recent Articles

Previous post New Year, New Hires: Choosing a Talent Tech Stack for 2018 February 2, 2018
Next post Sickweather March 23, 2018