Conquering ReactiveSwift: Signal and Observer (Part 3)

Susmita Horrow
Fueled Engineering
Published in
4 min readFeb 5, 2018

Hello folks! Welcome to part 3 of Conquering ReactiveSwift. In the previous article, we discussed various primitives of ReactiveSwift. In this part, we will discuss about Signal, which is an important primitive under the category Source. This article will guide you through the process of creating a Signal and observing it via an Observer.

Signal

In Functional Reactive Programming (FRP), we model our systems as time varying functions. This simply means that we define how the system is going to behave as the time passes. In contrast to Imperative Programming, where we manage state of a system at a given point of time, in FRP, here we deal with the changes in the state over a period of time. This concept of “change over time” is beautifully captured in the notion of Signal. A Signal is defined as a stream of events, where each Event represents the state of a system at a given point of time. An Event, the basic unit of a Signal, can be of one of the following kinds:

  • Value: valid information of any type
  • Failed: indicates that the stream has finished with an error
  • Completed: indicates the successful end of a stream, where no further events will be emitted
  • Interrupted: indicates that event production has been interrupted

A Signal continues to send a stream of events of type “Value” until it encounters an event of type “Failed/Completed/Interrupted”. Once the signal emits an event of type “Error/Completed/Interrupted”, it stops sending any value.

Observer

In order to observe the events emitted by a Signal, ReactiveSwift provides a primitive called Observer. An Observer is a simple wrapper around a closure of type (Event) -> Void. It encapsulates the information the behaviour of the system in response to events emitted by a Signal. Suppose we want to observe a Signal emitting integer values. We would define an Observer as follows:

let observer = Signal<Int, NoError>.Observer { (event) in
switch event {
case let .value(v):
print("value = \(v)")
case let .failed(error):
print("error = \(error)")
case .completed:
print("completed")
case .interrupted:
print("interrupted")
}
}

Now, we are clear about how to create an Observer, Now let’s learn how to create and observe a signal.

Let’s begin with a problem statement.

Print a message of time elapsed on every five seconds interval for fifty seconds.

In FRP, we model systems in terms of time-varying functions. In other words, we need to think in terms of Signals. For this problem, we need a Signal which will emit an integer value in every five seconds.

In order to achieve this, let’s get a pipe.

Signal creation can be visualised as a stream of water flowing through a pipe. The consumer deals with the receiving end of the pipe, and the producer sends the data in the source end.

In ReactiveSwift, a signal is created as follows:

Create a pipe

Here, the output is of type Signal<Int, NoError> and input is of type Observer<Int, NoError>.

input is used to inject event into the output.

Now let’s send value to the signal.

Send value to the signal stream

The signal is ready now. So let’s go ahead and observe it. For this, we need an instance of Observer. In the documentation, Observer is defined as follows:

An Observer is a simple wrapper around a function which can receive Events (typically from a Signal).

The consumer of a signal have to define a closure which encapsulates the actions to be performed when an event is received. Here we need a closure which prints time elapsed in response to the signal we created earlier. Let’s create an observer.

Create an observer

Let’s put things together.

The complete code

How does it work?

When we create a signal through pipe, we get a tuple of <output: Signal, input: Observer>. The input (of type Observer) represents all the subscribers of the signal. When we want to send an event to signal, we invoke send(value:Value) of the input. This will trigger send of all the subscribed observer and the closure associated with the observers will be executed.

Lifetime of a Signal:

A Signal ceases to exist when:

  1. It receives a terminating event
  2. There are no active observers and the signal is not retained

Lifetime of an observation:

We can define the how long we want to listen to the signal by using the primitives disposable and lifetime.

Alternative observation methods:

ReactiveSwift also provides following methods of observation on the Signal class. These are helpful to observe particular type of event.

  1. observeValues
  2. observeFailed
  3. observeInterrupted
  4. observeCompleted

Conclusion:

Let’s recap. We need three simple steps to create a signal and observe it:

  1. Get a pipe
  2. Send values via input observer
  3. Listen values via output observer

Sample code can be found here. In the next article, we will introduce the concept of SignalProducers.

Thank you for reading 😄

--

--