Skip to content

Instantly share code, notes, and snippets.

@zxzxlch
Last active August 5, 2016 15:19
Show Gist options
  • Save zxzxlch/2ff8395a5b9853d2d9a5e017693bd6a0 to your computer and use it in GitHub Desktop.
Save zxzxlch/2ff8395a5b9853d2d9a5e017693bd6a0 to your computer and use it in GitHub Desktop.
Using ReactiveCocoa 4.0
  • RAC4 breaks the concept of signals into two distinct types: SignalProducers and Signals. A SignalProducer is very similar to the old RACSignal; it does nothing until it is started. The developers changed the terminology from subscribed to started because it more accurately describes the state of the signal. For example, a SignalProducer could have multiple observers that are set up to receive events sent on the signal, but the signal won’t become active and the observers won’t see any events until it is started by something. A signal is started by calling start() on it.

  • The other new type is Signal. A Signal is like a firehose. You hook it up to a source, and events travel through it. There is no starting of a Signal, so you can’t use it to kick off work in the same way a SignalProducer can. Signals also can’t maintain any history. When something begins observing a Signal’s events, it will receive all future events sent on the signal but nothing from the past. Signals are really useful for carrying real-time information that originates from user interactions with the app. For example, a Signal might send the characters that a user enters into a text field.


  • Chaining syntax:

    /// RAC 3
    signal 
      |> filter { $0 % 2 == 0 } 
      |> map { $0 * $0 } 
      |> observe { print($0) }
    
    /// RAC 4
    signal
      .filter { $0 % 2 == 0 }
      .map { $0 * $0 }
      .observeNext { print($0) }
  • Hot signals are now Signals

    In the terminology of RAC 2, a “hot” RACSignal does not trigger any side effects when a -subscribe… method is called upon it. In other words, hot signals are entirely producer-driven and push-based, and consumers (subscribers) cannot have any effect on their lifetime.

    This pattern is useful for notifying observers about events that will occur no matter what. For example, a loading boolean might flip between true and false regardless of whether anything is observing it.

    Concretely, every RACSubject is a kind of hot signal, because the events being forwarded are not determined by the number of subscribers on the subject.

    In RAC 3, “hot” signals are now solely represented by the Signal class, and “cold” signals have been separated into their own type. This reduces complexity by making it clear that no Signal object can trigger side effects when observed.

  • Cold signals are now SignalProducers

    In the terminology of RAC 2, a “cold” RACSignal performs its work one time for every subscription. In other words, cold signals perform side effects when a -subscribe… method is called upon them, and may be able to cancel in-progress work if -dispose is called upon the returned RACDisposable.

    This pattern is broadly useful because it minimizes unnecessary work, and allows operators like take, retry, concat, etc. to manipulate when work is started and cancelled. Cold signals are also similar to how futures and promises work, and can be useful for structuring asynchronous code (like network requests).

    In RAC 3, “cold” signals are now solely represented by the SignalProducer class, which clearly indicates their relationship to “hot” signals. As the name indicates, a signal producer is responsible for creating a signal (when started), and can perform work as part of that process—meanwhile, the signal can have any number of observers without any additional side effects.

  • Because actions are frequently used in conjunction with AppKit or UIKit, there is also a CocoaAction class that erases the type parameters of an Action, allowing it to be used from Objective-C.

    As an example, an action can be wrapped and bound to UIControl like so:

    self.cocoaAction = CocoaAction(underlyingAction)
    self.button.addTarget(self.cocoaAction, action: CocoaAction.selector, forControlEvents: UIControlEvents.TouchUpInside)

Hot Observables Cold observables
... are sequences ... are sequences
Use resources ("produce heat") no matter if there is any observer subscribed. Don't use resources (don't produce heat) until observer subscribes.
Variables / properties / constants, tap coordinates, mouse coordinates, UI control values, current time Async operations, HTTP Connections, TCP connections, streams
Usually contains ~ N elements Usually contains ~ 1 element
Sequence elements are produced no matter if there is any observer subscribed. Sequence elements are produced only if there is a subscribed observer.
Sequence computation resources are usually shared between all of the subscribed observers. Sequence computation resources are usually allocated per subscribed observer.
Usually stateful Usually stateless

  • If you assume you are dealing with a hot signal and it turns out to be a cold one, you will be starting side effects for each new subscriber. This can have tremendous effects in your application. A common example, would be three or four entities in your app wanting to observe a network request and for each new subscription a different request would be started.

  • Initially you will feel that most of the operators don't fit your needs, but as you learn more, you will see that in fact they do. Rarely I had the need to create new operators. My recommendation is to make an extra effort and exhaust your options, before creating a new one.

  • 99% of the time you won't care about hot and cold signals. Until you do and lose hours of work. Take a bit of time to understand the difference.

  • You will feel tremendously tempted to use a given result (e.g. [FooBars]) inside the startWithNext closure. Always strive for binding, instead of setting values manually. Binding, not only makes the code more readable, it also makes memory management easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment