doOn in action

Let's work with doOn in code. We will use the same playground page, and start by commenting out the implementation so far. We will create a bare-bones implementation of the method for doOn and inside the implementation we will declare a disposeBag, as follows:

executeProcedure(for: "do(on....:)") { 

let disposeBag = DisposeBag()

}

Then, we will create a PublishSubject of the Int type:

let temperatureInFahrenheit = PublishSubject<Int>()

This PublishSubject will hold values that are temperatureInFahrenheit that we want to convert to Celsius.

We will chain a do(onNext:) operator on to the sequence and in it, multiply the element by itself. Although we could work with the result of this calculation within the do(onNext:) handler, it does not transform the element as a map operator would:

let temperatureInFahrenheit = PublishSubject<Int>()

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})

The next event is passed through exactly as-is. We will chain in another do(onNext:) operator, this time to print out the beginning part of our message containing the element as a value in degrees Fahrenheit. We will select the special symbol for degrees Fahrenheit by selecting Edit | Emoji & Symbols from the menu and searching for degree symbol, as shown:

Double-click on the degree Fahrenheit symbol to access the symbol in the code:

let temperatureInFahrenheit = PublishSubject<Int>()

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0)℉ = ", terminator: "")
})

Next, we will use map to transform the element from Fahrenheit to Celsius, as shown in this code:

let temperatureInFahrenheit = PublishSubject<Int>()

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}

Also, before we subscribe, we will add another call to doOn, just to show that we can include multiple handlers; onError receives an error instance, which we will print out, as shown in the code, with other events as well:

let temperatureInFahrenheit = PublishSubject<Int>()

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})

We will then subscribe to next events and print out the transform values as degrees Celsius:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {

})

We are using a format specifier here to print only one decimal place, and we will use the shortcut, that is, control + option + Space, to bring up the emoji picker this time:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {
print(String(format: "%.1f", $0))
})

Of course, we will add the subscription to disposeBag:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {
print(String(format: "%.1f", $0))
})
.disposed(by: disposeBag)

Now we will add a few values to temperatureInFahrenheit, as follows:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {
print(String(format: "%.1f", $0))
})
.disposed(by: disposeBag)

temperatureInFahrenheit.onNext(-40)
temperatureInFahrenheit.onNext(0)
temperatureInFahrenheit.onNext(37)

The result of the additions can be noted in the console:

Each Fahrenheit value is converted to Celsius and as per the formula; -40℉ is equal to -40℃, strange aye!

The results are then followed by the dispose message. As you can see in the console, there was no error emitted during the emission of events.

We have reinforced the notion throughout the book of disposing subscriptions to a dispose bag. Let's comment out the statement where we add the subscription to dispose bag, as follows:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {
print(String(format: "%.1f", $0))
})
// .disposed(by: disposeBag)

temperatureInFahrenheit.onNext(-40)
temperatureInFahrenheit.onNext(0)
temperatureInFahrenheit.onNext(37)

As a result, the subscription will not be disposed; you can see this in the console:

Neither the completed, nor the disposed message is printed, which means that we have leaked memory.

Instead, we will terminate the sequence by calling onCompleted on it:

temperatureInFahrenheit
.do(onNext: {
$0 * $0
})
.do(onNext: {
print("\($0) = ", terminator: "")
})
.map{
Double($0 - 32) * 5 / 9.0
}
.do(onError: {
print($0)
},
onCompleted: {
print("Completed the sequence")
},
onSubscribe: {
print("Subscribed to sequence")
},
onDispose: {
print("Sequence Disposed")
})
.subscribe(onNext: {
print(String(format: "%.1f", $0))
})
// .disposed(by: disposeBag)

temperatureInFahrenheit.onNext(-40)
temperatureInFahrenheit.onNext(0)
temperatureInFahrenheit.onNext(37)

temperatureInFahrenheit.onCompleted()

You will note that it is now terminated and properly disposed of; you can check this in the console:

So far, all the examples we have shown you have been performed on the main thread. We will go over how to manage concurrency in reactive programming using schedulers in upcoming chapters.

We have covered a number of operators so far, and the way we can work with several Observable sequences using these operators. You might be wondering how to use these concepts in a real-world application and hence we thought that it would be nice if we bound these concepts together and created a small application using the knowledge we have gained so far. In the next section, we will create a small application to address the concepts we have learned so far.

We will start with a new project in this section and build an application named Mother_Earth.