Skip to content

Instantly share code, notes, and snippets.

@andersio
Last active July 4, 2016 07:08
Show Gist options
  • Save andersio/9a3e1da44f986ab8fd64b8b2bbbe1859 to your computer and use it in GitHub Desktop.
Save andersio/9a3e1da44f986ab8fd64b8b2bbbe1859 to your computer and use it in GitHub Desktop.
Revisiting `startWith`.
Changes to the Protocol Hierarchy
  ----------------------------         --------------------------        
  | SignalProducerObservable |    ←    | SignalProducerProtocol |  
  ----------------------------         --------------------------  
               ↑                                   ↑  
  ---------------------------              ------------------  
  | FinalizedSignalProducer |              | SignalProducer |  
  ---------------------------              ------------------  
  

SignalProducerObservable describes a producer that is observable for events. All the observation APIs and starting APIs are available in this protocol. SignalProducerProtocol inherits SignalProducerObservable, and provides compositional operators.

Examples
Observations
// The intention to start a producer must be explicitly stated.  
property.producer.observeValues { print("\($0)") }.start()  
  
// Bonus: Xcode can indent this properly!  
property.producer  
    .observeValues { print("\($0)") }  
    .start()  
Chaining observations
// Bonus: Xcode can indent this properly too!  
property.producer  
    .observeValues {  
        print("New value: \($0)")  
    }  
    .observeCompletion {  
        print("Completed!")  
    }  
    .start()  
Warnings & Errors
// WARNING: Unused result.  
property.producer  
    .observeValues { print("New value: \($0)") }  
  
// ERROR: `FinalizedSignalProducer` does not have a method named `map`.  
property.producer  
    .observeValues { print("New value: \($0)") }  
    .map { "\($0)" }  

🎉 🎉 🎉

Changes to the extension methods
  1. Replaces SignalProducerProtocol.start(_:) and SignalProducerProtocol.startWith* with SignalProducerObservable.observe*, and they no longer starts the producer in place. They return a FinalizedSignalProducer instead.

  2. Repurposes start to express the intention to start a producer. (i.e. no longer takes any observers or actions)

  3. Renames (Next|Result|Failed|Completed|Interruption) to (Values|Results|Failure|Completion|Interrupted). (Note that the Event enum cases need not be changed.)

FinalizedSignalProducer

Whenever a observe* method is called on a producer, it would always return a finalised producer. The difference from a non-finalised one is the inability of further composition, as FinalizedSignalProducer conforms to only SignalProducerObservable. However, the SignalProducerObservable.observe* calls are allowed to be chained, since they are all lazily attached to the produced signal.

Injecting side effects should use the on operator instead.

Why not implement this in SignalProducer directly?

It was attempted in an early prototype. But then tests show that the observers would be dropped if the compositional operators and the lazy observations are interleaved, except for those registered right before calling start.

An approach of type erasing observers was tried. But it spoiled the SignalProducer constructing/composing APIs, and is prone to error if the implementor of an operator does not call the appropriate initializer dedicated to producer operators.

Using SignalProducer(on:) should be feasible, but it is relatively heavyweight.

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