FAQ on ReactiveCocoa

Fundamental Concepts

Q: What is ReactiveCocoa?

ReactiveCocoa is an FRP (Functional Reactive Programming) framework particularly used for iOS. FRP = reactive programming built on the components of functional concepts.

References:

P/s: For convenience, we sometimes use the abbreviation RAC to refer to ReactiveCocoa.

Q: What is ReactiveSwift?

Prior to RAC 5.0, there had been only 1 module ReactiveCocoa to contain the whole source code. In RAC 5.0, they separated the core components to a new module called ReactiveSwift. And ReactiveCocoa now acts as a wrapper of reactive concepts with Cocoa frameworks (in order to work with NSObject, UI classes, binding stuff…).

P/s: In this page, when I mention RAC, I implicitly mean the 2 modules (ReactiveCocoa & ReactiveSwift) as a whole. Just forget the separation in this FAQ.

Q: What can we do with ReactiveCocoa?

Using RAC, we could eliminate some old-fashioned technology stacks/programming patterns:

Additionally, RAC could help us save a lot effort to implement:

Q: What is a stream?

A stream is a sequence of ongoing events ordered in time.

Q: What can be considered a stream?

Anything! From now on, you have to keep in mind that everything can be a stream.

Again, remember, everything can be a stream. Let your mind free (big grin)

Q: What aspects of streams should we care about?

We should care about the life cycle of a stream:

Alt text

Besides, we should acknowledge some operations on streams:

Note that streams are immutable (a basic principle in Functional Programming). Therefore, operations on streams always return a new stream. For ex: the code bellow will return another SignalProducer: stringProducer.filter { !$0.isEmpty }

Q: What alternatives could we have for RAC?

RxSwift is a really qualified candidate. It commenced later than RAC but it has been drawing much attention. From my observation, brand new projects tend to adopt RxSwift due to the active community. Besides, this community also ports the FRP to other platforms (Go, Android, .NET…), therefore, the contribution is more actively.

Btw, there is an article explaining why they upgrade their project from RAC 2.x → RxSwift. It’s worth a look: http://artsy.github.io/blog/2015/12/08/reactive-cocoa-to-rxswift/

Q: When should we use it?

We could use it aggressively in different use cases. Here are some that I always prefer RAC:

Signal and SignalProducer

Q: What is Signal? What is SignalProducer?

Singal and SignalProducer are 2 implementations of streams. The bare difference between them is that Signal represents hot observable while SignalProducer refers to cold observable. I personally prefer SignalProducer over Signal.

Q: I created a SignalProducer, I added some observers, but none of them are executed. What happened?

Perhaps you forgot to start the producer. We need to start it manually, it’s cold observable, remember?

Q: It’s hard to debug with RAC. How to troubleshoot issues effectively?

The easiest way which I often use is printing log to verify if a value arrived, (or) the stream completed, (or) any error occurred… Well, RAC provides a built-in function logEvents for debugging. You could also write your own functions :D

let loginProducer = login(email: email, password: password)
  .logEvents(identifier: "--- Login", events: [.started, .value, .completed, .failed])
let signupProducer = signup(email: email, password: password)
  .logWhenStarted("--- Signup. Started")
  .logWhenNext({"--- Signup. Next: \($0)"})
  .logWhenFailed({"--- Signup. Failed with error: \($0)"})

Operators

Q: What’s the difference between the 2 operators “then” and “flatMap”?

Now we compare the 2 implementations. Assume that we do not care about the returned values of the producer. Given that: downloadUrl1() and downloadUrl2() is of type SignalProducer<Void, NSError>

Impl 1:	// Using `then`
    downloadUrl1().then(downloadUrl2())	
Impl 2:	// Using `flatMap`
    downloadUrl1.flatMap(.latest) { _ in downloadUrl2() }

Discussion: As a best practice, for this kind of download, we often call sendCompleted() immediately after sendNext(). So basically, the 2 implementations above are considered the same.

Q: How to cancel a SignalProducer (from the outside)?

Manually dispose of the producer

let disposable = producer.start()	// Starting a producer returns a disposable
...
// You manually dispose it by calling .dispose()
disposable.dispose()	
...
// You could also add it to a parent composite disposable.
// When the parent is disposed, it triggers disposing of the attached disposables
parentCompositeDisposable.add(disposable)	

Or trigger by another signal/producer, like this

producer.take(until: cancelProducer)	// cancelProducer = cancelButton.rac_tap