Commit 6c37b5ef authored by Justin Spahr-Summers's avatar Justin Spahr-Summers
Browse files

Merge pull request #1382 from ReactiveCocoa/swift-development

The Great Swiftening (a.k.a. the new 3.0)
Showing with 3202 additions and 1413 deletions
+3202 -1413
......@@ -7,3 +7,9 @@
[submodule "Carthage/Checkouts/xcconfigs"]
path = Carthage/Checkouts/xcconfigs
url = https://github.com/jspahrsummers/xcconfigs.git
[submodule "Carthage/Checkouts/Result"]
path = Carthage/Checkouts/Result
url = https://github.com/antitypical/Result.git
[submodule "Carthage/Checkouts/Box"]
path = Carthage/Checkouts/Box
url = https://github.com/robrix/Box.git
language: objective-c
osx_image: xcode6.4
before_install: true
install: true
git:
submodules: false
script: script/cibuild
notifications:
email: false
slack:
secure: C9QTry5wUG9CfeH3rm3Z19R5rDWqDO7EhHAqHDXBxT6CpGRkTPFliJexpjBYB4sroJ8CiY5ZgTI2sjRBiAdGoE5ZQkfnwSoKQhWXkwo19TnbSnufr3cKO2SZkUhBqOlZcA+mgfjZ7rm2wm7RhpCR/4z8oBXDN4/xv0U5R2fLCLE=
This diff is collapsed.
github "antitypical/Result" ~> 0.4.4
github "jspahrsummers/xcconfigs" >= 0.7.1
github "Quick/Quick"
github "Quick/Nimble"
github "jspahrsummers/xcconfigs" ~> 0.8
github "Quick/Quick" ~> 0.3
github "Quick/Nimble" ~> 1.0.0
github "Quick/Nimble" "v0.4.2"
github "robrix/Box" "1.2.2"
github "Quick/Nimble" "v1.0.0"
github "Quick/Quick" "v0.3.1"
github "jspahrsummers/xcconfigs" "0.7.2"
github "jspahrsummers/xcconfigs" "0.8.1"
github "antitypical/Result" "0.4.4"
Subproject commit bbe4e612a03ffe0bbb0e2e476c2be4534b6777a5
Subproject commit 8927113f877f32c8a01b41b746c4ac261b42c48e
Subproject commit 61697d0b11bc5160fd620737365dcdd31cd5cf86
Subproject commit 81b189645babd30c6f70ed3f82a83988a467fb02
Subproject commit 2e77204b59c3d97c24e5dd34966fb32c231194f0
Subproject commit 99624a6af366c015b678a1135e4c558776a59be6
This diff is collapsed.
This diff is collapsed.
# Differences from Rx
ReactiveCocoa (RAC) is significantly inspired by .NET's [Reactive
Extensions](http://msdn.microsoft.com/en-us/data/gg577609.aspx) (Rx), but it is not
a direct port. Some concepts or interfaces presented in RAC may be initially
confusing to a developer already familiar with Rx, but it's usually possible to
express the same algorithms.
Some of the differences, like the naming of methods and classes, are meant to
keep RAC in line with existing Cocoa conventions. Other differences are intended
as improvements over Rx, or may be inspired by other functional reactive
programming paradigms (like the [Elm programming
language](http://elm-lang.org)).
Here, we'll attempt to document the high-level differences between RAC and Rx.
## Interfaces
RAC does not offer protocols that correspond to the `IEnumerable` and
`IObservable` interfaces in .NET. Instead, the functionality is covered by three
main classes:
* **[RACStream](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/RACStream.h)**
is an abstract class that implements stream operations using a few basic
primitives. The equivalents to generic LINQ operators can generally be found
on this class.
* **[RACSignal](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/RACSignal.h)**
is a concrete subclass of `RACStream` that implements a _push-driven_ stream,
much like `IObservable`. Time-based operators, or methods dealing with the
`completed` and `error` events, can be found on this class or in the
[RACSignal+Operations](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/RACSignal%2BOperations.h)
category upon it.
* **[RACSequence](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/RACSequence.h)**
is a concrete subclass of `RACStream` that implements a _pull-driven_ stream,
much like `IEnumerable`.
## Names of Stream Operations
RAC generally uses LINQ-style naming for its stream methods. Most of the
exceptions are inspired by significantly better alternatives in Haskell or Elm.
Notable differences include:
* `-map:` instead of `Select`
* `-filter:` instead of `Where`
* `-flatten` instead of `Merge`
* `-flattenMap:` instead of `SelectMany`
LINQ operators that go by different names in RAC (but behave more or less
equivalently) will be referenced from method documentation, like so:
```objc
// Maps `block` across the values in the receiver.
//
// This corresponds to the `Select` method in Rx.
//
// Returns a new stream with the mapped values.
- (instancetype)map:(id (^)(id value))block;
```
......@@ -8,196 +8,206 @@ learning about new modules and finding more specific documentation.
For examples and help understanding how to use RAC, see the [README][] or
the [Design Guidelines][].
## Streams
A **stream**, represented by the [RACStream][] abstract class, is any series of
object values.
Values may be available immediately or in the future, but must be retrieved
sequentially. There is no way to retrieve the second value of a stream without
evaluating or waiting for the first value.
Streams are [monads][]. Among other things, this allows complex operations to be
built on a few basic primitives (`-bind:` in particular). [RACStream][] also
implements the equivalent of the [Monoid][] and [MonadZip][] typeclasses from
[Haskell][].
[RACStream][] isn't terribly useful on its own. Most streams are treated as
[signals](#signals) or [sequences](#sequences) instead.
## Events
An **event**, represented by the [`Event`][Event] type, is the formalized representation
of the fact that _something has happened_. In ReactiveCocoa, events are the centerpiece
of communication. An event might represent the press of a button, a piece
of information received from an API, the occurrence of an error, or the completion
of a long-running operation. In any case, something generates the events and sends them over a
[signal](#signals) to any number of [observers](#observers).
`Event` is an enumerated type representing either a value or one of three
terminal events:
* The `Next` event provides a new value from the source.
* The `Error` event indicates that an error occurred before the signal could
finish. Events are parameterized by an `ErrorType`, which determines the kind
of error that’s permitted to appear in the event. If an error is not
permitted, the event can use type `NoError` to prevent any from being
provided.
* The `Completed` event indicates that the signal finished successfully, and
that no more values will be sent by the source.
* The `Interrupted` event indicates that the signal has terminated due to
cancellation, meaning that the operation was neither successful nor
unsuccessful.
## Signals
A **signal**, represented by the [RACSignal][] class, is a _push-driven_
[stream](#streams).
Signals generally represent data that will be delivered in the future. As work
is performed or data is received, values are _sent_ on the signal, which pushes
them out to any subscribers. Users must [subscribe](#subscription) to a signal
in order to access its values.
Signals send three different types of events to their subscribers:
A **signal**, represented by the [`Signal`][Signal] type, is any series of [events](#events)
over time that can be observed.
Signals are generally used to represent event streams that are already “in progress”,
like notifications, user input, etc. As work is performed or data is received,
events are _sent_ on the signal, which pushes them out to any observers.
All observers see the events at the same time.
Users must [observe](#observers) a signal in order to access its events.
Observing a signal does not trigger any side effects. In other words,
signals are entirely producer-driven and push-based, and consumers (observers)
cannot have any effect on their lifetime. While observing a signal, the user
can only evaluate the events in the same order as they are sent on the signal. There
is no random access to values of a signal.
Signals can be manipulated by applying [primitives][BasicOperators] to them.
Typical primitives to manipulate a single signal like `filter`, `map` and
`reduce` are available, as well as primitives to manipulate multiple signals
at once (`zip`). Primitives operate only on the `Next` events of a signal.
The `|>` operator is used to apply primitives to a signal. It can also be used
to compose basic primitives into more complex ones.
The lifetime of a signal consists of any number of `Next` events, followed by
one terminating event, which may be any one of `Error`, `Completed`, or
`Interrupted` (but not a combination).
Terminating events are not included in the signal’s values—they must be
handled specially.
### Pipes
A **pipe**, created by `Signal.pipe()`, is a [signal](#signals)
that can be manually controlled.
* The **next** event provides a new value from the stream. [RACStream][]
methods only operate on events of this type. Unlike Cocoa collections, it is
completely valid for a signal to include `nil`.
* The **error** event indicates that an error occurred before the signal could
finish. The event may include an `NSError` object that indicates what went
wrong. Errors must be handled specially – they are not included in the
stream's values.
* The **completed** event indicates that the signal finished successfully, and
that no more values will be added to the stream. Completion must be handled
specially – it is not included in the stream of values.
The method returns a [signal](#signals) and an [observer](#observers).
The signal can be controlled by sending events to the observer. This
can be extremely useful for bridging non-RAC code into the world of signals.
The lifetime of a signal consists of any number of `next` events, followed by
one `error` or `completed` event (but not both).
For example, instead of handling application logic in block callbacks, the
blocks can simply send events to the observer instead. Meanwhile, the signal
can be returned, hiding the implementation detail of the callbacks.
### Subscription
## Signal Producers
A **subscriber** is anything that is waiting or capable of waiting for events
from a [signal](#signals). Within RAC, a subscriber is represented as any object
that conforms to the [RACSubscriber][] protocol.
A **signal producer**, represented by the [`SignalProducer`][SignalProducer] type, creates
[signals](#signals) and performs side effects.
A **subscription** is created through any call to
[-subscribeNext:error:completed:][RACSignal], or one of the corresponding
convenience methods. Technically, most [RACStream][] and
[RACSignal][RACSignal+Operations] operators create subscriptions as well, but
these intermediate subscriptions are usually an implementation detail.
They can be used to represent operations or tasks, like network
requests, where each invocation of `start()` will create a new underlying
operation, and allow the caller to observe the result(s). The
`startWithSignal()` variant gives access to the produced signal, allowing it to
be observed multiple times if desired.
Subscriptions [retain their signals][Memory Management], and are automatically
disposed of when the signal completes or errors. Subscriptions can also be
[disposed of manually](#disposables).
Because of the behavior of `start()`, each signal created from the same
producer may see a different ordering or version of events, or the stream might
even be completely different! Unlike a plain signal, no work is started (and
thus no events are generated) until an observer is attached, and the work is
restarted anew for each additional observer.
### Subjects
Starting a signal producer returns a [disposable](#disposables) that can be used to
interrupt/cancel the work associated with the produced signal.
A **subject**, represented by the [RACSubject][] class, is a [signal](#signals)
that can be manually controlled.
Just like signals, signal producers can also be manipulated via primitives
like `map`, `filter`, etc.
Every signal primitive can be “lifted” to operate upon signal producers instead,
using the `lift` method, or implicitly through the `|>` operator.
Furthermore, there are additional primitives that control _when_ and _how_ work
is started—for example, `times`.
Subjects can be thought of as the "mutable" variant of a signal, much like
`NSMutableArray` is for `NSArray`. They are extremely useful for bridging
non-RAC code into the world of signals.
### Buffers
For example, instead of handling application logic in block callbacks, the
blocks can simply send events to a shared subject instead. The subject can then
be returned as a [RACSignal][], hiding the implementation detail of the
callbacks.
A **buffer**, created by `SignalProducer.buffer()`, is a (optionally bounded)
queue for [events](#events) that replays those events when new
[signals](#signals) are created from the producer.
Some subjects offer additional behaviors as well. In particular,
[RACReplaySubject][] can be used to buffer events for future
[subscribers](#subscription), like when a network request finishes before
anything is ready to handle the result.
Similar to a [pipe](#pipes), the method returns an [observer](#observers).
Events sent to this observer will be added to the queue. If the buffer is already
at capacity when a new value arrives, the earliest (oldest) value will be
dropped to make room for it.
### Commands
## Observers
A **command**, represented by the [RACCommand][] class, creates and subscribes
to a signal in response to some action. This makes it easy to perform
side-effecting work as the user interacts with the app.
An **observer** is anything that is waiting or capable of waiting for [events](#events)
from a [signal](#signals). Within RAC, an observer is represented as
a [`SinkType`](http://swiftdoc.org/protocol/SinkType/) that accepts
[`Event`][Event] values.
Usually the action triggering a command is UI-driven, like when a button is
clicked. Commands can also be automatically disabled based on a signal, and this
disabled state can be represented in a UI by disabling any controls associated
with the command.
Observers can be implicitly created by using the callback-based versions of the
`Signal.observe` or `SignalProducer.start` methods.
On OS X, RAC adds a `rac_command` property to
[NSButton][NSButton+RACCommandSupport] for setting up these behaviors
automatically.
## Actions
### Connections
An **action**, represented by the [`Action`][Action] type, will do some work when
executed with an input. While executing, zero or more output values and/or an
error may be generated.
A **connection**, represented by the [RACMulticastConnection][] class, is
a [subscription](#subscription) that is shared between any number of
subscribers.
Actions are useful for performing side-effecting work upon user interaction, like when a button is
clicked. Actions can also be automatically disabled based on a [property](#properties), and this
disabled state can be represented in a UI by disabling any controls associated
with the action.
[Signals](#signals) are _cold_ by default, meaning that they start doing work
_each_ time a new subscription is added. This behavior is usually desirable,
because it means that data will be freshly recalculated for each subscriber, but
it can be problematic if the signal has side effects or the work is expensive
(for example, sending a network request).
For interaction with `NSControl` or `UIControl`, RAC provides the
[`CocoaAction`][CocoaAction] type for bridging actions to Objective-C.
A connection is created through the `-publish` or `-multicast:` methods on
[RACSignal][RACSignal+Operations], and ensures that only one underlying
subscription is created, no matter how many times the connection is subscribed
to. Once connected, the connection's signal is said to be _hot_, and the
underlying subscription will remain active until _all_ subscriptions to the
connection are [disposed](#disposables).
## Properties
## Sequences
A **property**, represented by the [`PropertyType`][Property] protocol,
stores a value and notifies observers about future changes to that value.
A **sequence**, represented by the [RACSequence][] class, is a _pull-driven_
[stream](#streams).
The current value of a property can be obtained from the `value` getter. The
`producer` getter returns a [signal producer](#signal-producers) that will send
the property’s current value, followed by all changes over time.
Sequences are a kind of collection, similar in purpose to `NSArray`. Unlike
an array, the values in a sequence are evaluated _lazily_ (i.e., only when they
are needed) by default, potentially improving performance if only part of
a sequence is used. Just like Cocoa collections, sequences cannot contain `nil`.
The `<~` operator can be used to bind properties in different ways. Note that in
all cases, the target has to be a [`MutablePropertyType`][Property].
Sequences are similar to [Clojure's sequences][seq] ([lazy-seq][] in particular), or
the [List][] type in [Haskell][].
* `property <~ signal` binds a [signal](#signals) to the property, updating the
property’s value to the latest value sent by the signal.
* `property <~ producer` starts the given [signal producer](#signal-producers),
and binds the property’s value to the latest value sent on the started signal.
* `property <~ otherProperty` binds one property to another, so that the destination
property’s value is updated whenever the source property is updated.
RAC adds a `-rac_sequence` method to most of Cocoa's collection classes,
allowing them to be used as [RACSequences][RACSequence] instead.
The [`DynamicProperty`][Property] type can be used to bridge to Objective-C APIs
that require Key-Value Coding (KVC) or Key-Value Observing (KVO), like
`NSOperation`. Note that most AppKit and UIKit properties do _not_ support KVO,
so their changes should be observed through other mechanisms.
[`MutableProperty`][Property] should be preferred over dynamic properties
whenever possible!
## Disposables
The **[RACDisposable][]** class is used for cancellation and resource cleanup.
A **disposable**, represented by the [`Disposable`][Disposable] protocol, is a a mechanism
for memory management and cancellation.
Disposables are most commonly used to unsubscribe from a [signal](#signals).
When a [subscription](#subscription) is disposed, the corresponding subscriber
will not receive _any_ further events from the signal. Additionally, any work
associated with the subscription (background processing, network requests, etc.)
will be cancelled, since the results are no longer needed.
When starting a [signal producer](#signal-producers), a disposable will be returned.
This disposable can be used by the caller to cancel the work that has been started
(e.g. background processing, network requests, etc.), clean up all temporary
resources, then send a final `Interrupted` event upon the particular
[signal](#signals) that was created.
Observing a [signal](#signals) may also return a disposable. Disposing it will
prevent the observer from receiving any future events from that signal, but it
will not have any effect on the signal itself.
For more information about cancellation, see the RAC [Design Guidelines][].
## Schedulers
A **scheduler**, represented by the [RACScheduler][] class, is a serial
execution queue for [signals](#signals) to perform work or deliver their results upon.
A **scheduler**, represented by the [`SchedulerType`][Scheduler] protocol, is a
serial execution queue to perform work or deliver results upon.
[Signals](#signals) and [signal producers](#signal-producers) can be ordered to
deliver events on a specific scheduler. [Signal producers](#signal-producers)
can additionally be ordered to start their work on a specific scheduler.
Schedulers are similar to Grand Central Dispatch queues, but schedulers support
cancellation (via [disposables](#disposables)), and always execute serially.
With the exception of the [+immediateScheduler][RACScheduler], schedulers do not
With the exception of the [`ImmediateScheduler`][Scheduler], schedulers do not
offer synchronous execution. This helps avoid deadlocks, and encourages the use
of [signal operators][RACSignal+Operations] instead of blocking work.
of [signal and signal producer primitives][BasicOperators] instead of blocking work.
[RACScheduler][] is also somewhat similar to `NSOperationQueue`, but schedulers
Schedulers are also somewhat similar to `NSOperationQueue`, but schedulers
do not allow tasks to be reordered or depend on one another.
## Value types
RAC offers a few miscellaneous classes for conveniently representing values in
a [stream](#streams):
* **[RACTuple][]** is a small, constant-sized collection that can contain
`nil` (represented by `RACTupleNil`). It is generally used to represent
the combined values of multiple streams.
* **[RACUnit][]** is a singleton "empty" value. It is used as a value in
a stream for those times when more meaningful data doesn't exist.
* **[RACEvent][]** represents any [signal event](#signals) as a single value.
It is primarily used by the `-materialize` method of
[RACSignal][RACSignal+Operations].
[Design Guidelines]: DesignGuidelines.md
[Haskell]: http://www.haskell.org
[lazy-seq]: http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/lazy-seq
[List]: https://downloads.haskell.org/~ghc/latest/docs/html/libraries/base-4.7.0.2/Data-List.html
[Memory Management]: MemoryManagement.md
[monads]: http://en.wikipedia.org/wiki/Monad_(functional_programming)
[Monoid]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/base-4.7.0.2/Data-Monoid.html
[MonadZip]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/base-4.7.0.2/Control-Monad-Zip.html
[NSButton+RACCommandSupport]: ../ReactiveCocoa/NSButton+RACCommandSupport.h
[RACCommand]: ../ReactiveCocoa/RACCommand.h
[RACDisposable]: ../ReactiveCocoa/RACDisposable.h
[RACEvent]: ../ReactiveCocoa/RACEvent.h
[RACMulticastConnection]: ../ReactiveCocoa/RACMulticastConnection.h
[RACReplaySubject]: ../ReactiveCocoa/RACReplaySubject.h
[RACScheduler]: ../ReactiveCocoa/RACScheduler.h
[RACSequence]: ../ReactiveCocoa/RACSequence.h
[RACSignal]: ../ReactiveCocoa/RACSignal.h
[RACSignal+Operations]: ../ReactiveCocoa/RACSignal+Operations.h
[RACStream]: ../ReactiveCocoa/RACStream.h
[RACSubject]: ../ReactiveCocoa/RACSubject.h
[RACSubscriber]: ../ReactiveCocoa/RACSubscriber.h
[RACTuple]: ../ReactiveCocoa/RACTuple.h
[RACUnit]: ../ReactiveCocoa/RACUnit.h
[BasicOperators]: BasicOperators.md
[README]: ../README.md
[seq]: http://clojure.org/sequences
[Signal]: ../ReactiveCocoa/Swift/Signal.swift
[SignalProducer]: ../ReactiveCocoa/Swift/SignalProducer.swift
[Action]: ../ReactiveCocoa/Swift/Action.swift
[CocoaAction]: ../ReactiveCocoa/Swift/Action.swift
[Disposable]: ../ReactiveCocoa/Swift/Disposable.swift
[Scheduler]: ../ReactiveCocoa/Swift/Scheduler.swift
[Property]: ../ReactiveCocoa/Swift/Property.swift
[Event]: ../ReactiveCocoa/Swift/Event.swift
[SinkOf]: http://swiftdoc.org/type/SinkOf/
# Basic Operators
This document explains some of the most common operators used in ReactiveCocoa,
and includes examples demonstrating their use.
Operators that apply to [sequences][Sequences] _and_ [signals][Signals] are
known as [stream][Streams] operators.
**[Performing side effects with signals](#performing-side-effects-with-signals)**
1. [Subscription](#subscription)
1. [Injecting effects](#injecting-effects)
**[Transforming streams](#transforming-streams)**
1. [Mapping](#mapping)
1. [Filtering](#filtering)
**[Combining streams](#combining-streams)**
1. [Concatenating](#concatenating)
1. [Flattening](#flattening)
1. [Mapping and flattening](#mapping-and-flattening)
**[Combining signals](#combining-signals)**
1. [Sequencing](#sequencing)
1. [Merging](#merging)
1. [Combining latest values](#combining-latest-values)
1. [Switching](#switching)
## Performing side effects with signals
Most signals start out "cold," which means that they will not do any work until
[subscription](#subscription).
Upon subscription, a signal or its [subscribers][Subscription] can perform _side
effects_, like logging to the console, making a network request, updating the
user interface, etc.
Side effects can also be [injected](#injecting-effects) into a signal, where
they won't be performed immediately, but will instead take effect with each
subscription later.
### Subscription
The [-subscribe…][RACSignal] methods give you access to the current and future values in a signal:
```objc
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
// Outputs: A B C D E F G H I
[letters subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
```
For a cold signal, side effects will be performed once _per subscription_:
```objc
__block unsigned subscriptions = 0;
RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];
// Outputs:
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
// Outputs:
// subscription 2
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
```
This behavior can be changed using a [connection][Connections].
### Injecting effects
The [-do…][RACSignal+Operations] methods add side effects to a signal without actually
subscribing to it:
```objc
__block unsigned subscriptions = 0;
RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];
// Does not output anything yet
loggingSignal = [loggingSignal doCompleted:^{
NSLog(@"about to complete subscription %u", subscriptions);
}];
// Outputs:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
```
## Transforming streams
These operators transform a single stream into a new stream.
### Mapping
The [-map:][RACStream] method is used to transform the values in a stream, and
create a new stream with the results:
```objc
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
// Contains: AA BB CC DD EE FF GG HH II
RACSequence *mapped = [letters map:^(NSString *value) {
return [value stringByAppendingString:value];
}];
```
### Filtering
The [-filter:][RACStream] method uses a block to test each value, including it
into the resulting stream only if the test passes:
```objc
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
// Contains: 2 4 6 8
RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) {
return (value.intValue % 2) == 0;
}];
```
## Combining streams
These operators combine multiple streams into a single new stream.
### Concatenating
The [-concat:][RACStream] method appends one stream's values to another:
```objc
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *concatenated = [letters concat:numbers];
```
### Flattening
The [-flatten][RACStream] operator is applied to a stream-of-streams, and
combines their values into a single new stream.
Sequences are [concatenated](#concatenating):
```objc
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[ letters, numbers ].rac_sequence;
// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *flattened = [sequenceOfSequences flatten];
```
Signals are [merged](#merging):
```objc
RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
[subscriber sendNext:letters];
[subscriber sendNext:numbers];
[subscriber sendCompleted];
return nil;
}];
RACSignal *flattened = [signalOfSignals flatten];
// Outputs: A 1 B C 2
[flattened subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];
```
### Mapping and flattening
[Flattening](#flattening) isn't that interesting on its own, but understanding
how it works is important for [-flattenMap:][RACStream].
`-flattenMap:` is used to transform each of a stream's values into _a new
stream_. Then, all of the streams returned will be flattened down into a single
stream. In other words, it's [-map:](#mapping) followed by [-flatten](#flattening).
This can be used to extend or edit sequences:
```objc
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence *extended = [numbers flattenMap:^(NSString *num) {
return @[ num, num ].rac_sequence;
}];
// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence *edited = [numbers flattenMap:^(NSString *num) {
if (num.intValue % 2 == 0) {
return [RACSequence empty];
} else {
NSString *newNum = [num stringByAppendingString:@"_"];
return [RACSequence return:newNum];
}
}];
```
Or create multiple signals of work which are automatically recombined:
```objc
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
[[letters
flattenMap:^(NSString *letter) {
return [database saveEntriesForLetter:letter];
}]
subscribeCompleted:^{
NSLog(@"All database entries saved successfully.");
}];
```
## Combining signals
These operators combine multiple signals into a single new [RACSignal][].
### Sequencing
[-then:][RACSignal+Operations] starts the original signal,
waits for it to complete, and then only forwards the values from a new signal:
```objc
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
// The new signal only contains: 1 2 3 4 5 6 7 8 9
//
// But when subscribed to, it also outputs: A B C D E F G H I
RACSignal *sequenced = [[letters
doNext:^(NSString *letter) {
NSLog(@"%@", letter);
}]
then:^{
return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
}];
```
This is most useful for executing all the side effects of one signal, then
starting another, and only returning the second signal's values.
### Merging
The [+merge:][RACSignal+Operations] method will forward the values from many
signals into a single stream, as soon as those values arrive:
```objc
RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *merged = [RACSignal merge:@[ letters, numbers ]];
// Outputs: A 1 B C 2
[merged subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];
```
### Combining latest values
The [+combineLatest:][RACSignal+Operations] and `+combineLatest:reduce:` methods
will watch multiple signals for changes, and then send the latest values from
_all_ of them when a change occurs:
```objc
RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *combined = [RACSignal
combineLatest:@[ letters, numbers ]
reduce:^(NSString *letter, NSString *number) {
return [letter stringByAppendingString:number];
}];
// Outputs: B1 B2 C2 C3
[combined subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[letters sendNext:@"A"];
[letters sendNext:@"B"];
[numbers sendNext:@"1"];
[numbers sendNext:@"2"];
[letters sendNext:@"C"];
[numbers sendNext:@"3"];
```
Note that the combined signal will only send its first value when all of the
inputs have sent at least one. In the example above, `@"A"` was never
forwarded because `numbers` had not sent a value yet.
### Switching
The [-switchToLatest][RACSignal+Operations] operator is applied to
a signal-of-signals, and always forwards the values from the latest signal:
```objc
RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSubject *signalOfSignals = [RACSubject subject];
RACSignal *switched = [signalOfSignals switchToLatest];
// Outputs: A B 1 D
[switched subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
[signalOfSignals sendNext:letters];
[letters sendNext:@"A"];
[letters sendNext:@"B"];
[signalOfSignals sendNext:numbers];
[letters sendNext:@"C"];
[numbers sendNext:@"1"];
[signalOfSignals sendNext:letters];
[numbers sendNext:@"2"];
[letters sendNext:@"D"];
```
[Connections]: FrameworkOverview.md#connections
[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h
[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h
[RACSignal+Operations]: ../../ReactiveCocoa/Objective-C/RACSignal+Operations.h
[RACStream]: ../../ReactiveCocoa/Objective-C/RACStream.h
[Sequences]: FrameworkOverview.md#sequences
[Signals]: FrameworkOverview.md#signals
[Streams]: FrameworkOverview.md#streams
[Subscription]: FrameworkOverview.md#subscription
This diff is collapsed.
# Framework Overview
This document contains a high-level description of the different components
within the ReactiveCocoa framework, and an attempt to explain how they work
together and divide responsibilities. This is meant to be a starting point for
learning about new modules and finding more specific documentation.
For examples and help understanding how to use RAC, see the [README][] or
the [Design Guidelines][].
## Streams
A **stream**, represented by the [RACStream][] abstract class, is any series of
object values.
Values may be available immediately or in the future, but must be retrieved
sequentially. There is no way to retrieve the second value of a stream without
evaluating or waiting for the first value.
Streams are [monads][]. Among other things, this allows complex operations to be
built on a few basic primitives (`-bind:` in particular). [RACStream][] also
implements the equivalent of the [Monoid][] and [MonadZip][] typeclasses from
[Haskell][].
[RACStream][] isn't terribly useful on its own. Most streams are treated as
[signals](#signals) or [sequences](#sequences) instead.
## Signals
A **signal**, represented by the [RACSignal][] class, is a _push-driven_
[stream](#streams).
Signals generally represent data that will be delivered in the future. As work
is performed or data is received, values are _sent_ on the signal, which pushes
them out to any subscribers. Users must [subscribe](#subscription) to a signal
in order to access its values.
Signals send three different types of events to their subscribers:
* The **next** event provides a new value from the stream. [RACStream][]
methods only operate on events of this type. Unlike Cocoa collections, it is
completely valid for a signal to include `nil`.
* The **error** event indicates that an error occurred before the signal could
finish. The event may include an `NSError` object that indicates what went
wrong. Errors must be handled specially – they are not included in the
stream's values.
* The **completed** event indicates that the signal finished successfully, and
that no more values will be added to the stream. Completion must be handled
specially – it is not included in the stream of values.
The lifetime of a signal consists of any number of `next` events, followed by
one `error` or `completed` event (but not both).
### Subscription
A **subscriber** is anything that is waiting or capable of waiting for events
from a [signal](#signals). Within RAC, a subscriber is represented as any object
that conforms to the [RACSubscriber][] protocol.
A **subscription** is created through any call to
[-subscribeNext:error:completed:][RACSignal], or one of the corresponding
convenience methods. Technically, most [RACStream][] and
[RACSignal][RACSignal+Operations] operators create subscriptions as well, but
these intermediate subscriptions are usually an implementation detail.
Subscriptions [retain their signals][Memory Management], and are automatically
disposed of when the signal completes or errors. Subscriptions can also be
[disposed of manually](#disposables).
### Subjects
A **subject**, represented by the [RACSubject][] class, is a [signal](#signals)
that can be manually controlled.
Subjects can be thought of as the "mutable" variant of a signal, much like
`NSMutableArray` is for `NSArray`. They are extremely useful for bridging
non-RAC code into the world of signals.
For example, instead of handling application logic in block callbacks, the
blocks can simply send events to a shared subject instead. The subject can then
be returned as a [RACSignal][], hiding the implementation detail of the
callbacks.
Some subjects offer additional behaviors as well. In particular,
[RACReplaySubject][] can be used to buffer events for future
[subscribers](#subscription), like when a network request finishes before
anything is ready to handle the result.
### Commands
A **command**, represented by the [RACCommand][] class, creates and subscribes
to a signal in response to some action. This makes it easy to perform
side-effecting work as the user interacts with the app.
Usually the action triggering a command is UI-driven, like when a button is
clicked. Commands can also be automatically disabled based on a signal, and this
disabled state can be represented in a UI by disabling any controls associated
with the command.
On OS X, RAC adds a `rac_command` property to
[NSButton][NSButton+RACCommandSupport] for setting up these behaviors
automatically.
### Connections
A **connection**, represented by the [RACMulticastConnection][] class, is
a [subscription](#subscription) that is shared between any number of
subscribers.
[Signals](#signals) are _cold_ by default, meaning that they start doing work
_each_ time a new subscription is added. This behavior is usually desirable,
because it means that data will be freshly recalculated for each subscriber, but
it can be problematic if the signal has side effects or the work is expensive
(for example, sending a network request).
A connection is created through the `-publish` or `-multicast:` methods on
[RACSignal][RACSignal+Operations], and ensures that only one underlying
subscription is created, no matter how many times the connection is subscribed
to. Once connected, the connection's signal is said to be _hot_, and the
underlying subscription will remain active until _all_ subscriptions to the
connection are [disposed](#disposables).
## Sequences
A **sequence**, represented by the [RACSequence][] class, is a _pull-driven_
[stream](#streams).
Sequences are a kind of collection, similar in purpose to `NSArray`. Unlike
an array, the values in a sequence are evaluated _lazily_ (i.e., only when they
are needed) by default, potentially improving performance if only part of
a sequence is used. Just like Cocoa collections, sequences cannot contain `nil`.
Sequences are similar to [Clojure's sequences][seq] ([lazy-seq][] in particular), or
the [List][] type in [Haskell][].
RAC adds a `-rac_sequence` method to most of Cocoa's collection classes,
allowing them to be used as [RACSequences][RACSequence] instead.
## Disposables
The **[RACDisposable][]** class is used for cancellation and resource cleanup.
Disposables are most commonly used to unsubscribe from a [signal](#signals).
When a [subscription](#subscription) is disposed, the corresponding subscriber
will not receive _any_ further events from the signal. Additionally, any work
associated with the subscription (background processing, network requests, etc.)
will be cancelled, since the results are no longer needed.
For more information about cancellation, see the RAC [Design Guidelines][].
## Schedulers
A **scheduler**, represented by the [RACScheduler][] class, is a serial
execution queue for [signals](#signals) to perform work or deliver their results upon.
Schedulers are similar to Grand Central Dispatch queues, but schedulers support
cancellation (via [disposables](#disposables)), and always execute serially.
With the exception of the [+immediateScheduler][RACScheduler], schedulers do not
offer synchronous execution. This helps avoid deadlocks, and encourages the use
of [signal operators][RACSignal+Operations] instead of blocking work.
[RACScheduler][] is also somewhat similar to `NSOperationQueue`, but schedulers
do not allow tasks to be reordered or depend on one another.
## Value types
RAC offers a few miscellaneous classes for conveniently representing values in
a [stream](#streams):
* **[RACTuple][]** is a small, constant-sized collection that can contain
`nil` (represented by `RACTupleNil`). It is generally used to represent
the combined values of multiple streams.
* **[RACUnit][]** is a singleton "empty" value. It is used as a value in
a stream for those times when more meaningful data doesn't exist.
* **[RACEvent][]** represents any [signal event](#signals) as a single value.
It is primarily used by the `-materialize` method of
[RACSignal][RACSignal+Operations].
[Design Guidelines]: DesignGuidelines.md
[Haskell]: http://www.haskell.org
[lazy-seq]: http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/lazy-seq
[List]: https://downloads.haskell.org/~ghc/latest/docs/html/libraries/Data-List.html
[Memory Management]: MemoryManagement.md
[monads]: http://en.wikipedia.org/wiki/Monad_(functional_programming)
[Monoid]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/Data-Monoid.html
[MonadZip]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/Control-Monad-Zip.html
[NSButton+RACCommandSupport]: ../../ReactiveCocoa/Objective-C/NSButton+RACCommandSupport.h
[RACCommand]: ../../ReactiveCocoa/Objective-C/RACCommand.h
[RACDisposable]: ../../ReactiveCocoa/Objective-C/RACDisposable.h
[RACEvent]: ../../ReactiveCocoa/Objective-C/RACEvent.h
[RACMulticastConnection]: ../../ReactiveCocoa/Objective-C/RACMulticastConnection.h
[RACReplaySubject]: ../../ReactiveCocoa/Objective-C/RACReplaySubject.h
[RACScheduler]: ../../ReactiveCocoa/Objective-C/RACScheduler.h
[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h
[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h
[RACSignal+Operations]: ../../ReactiveCocoa/Objective-C/RACSignal+Operations.h
[RACStream]: ../../ReactiveCocoa/Objective-C/RACStream.h
[RACSubject]: ../../ReactiveCocoa/Objective-C/RACSubject.h
[RACSubscriber]: ../../ReactiveCocoa/Objective-C/RACSubscriber.h
[RACTuple]: ../../ReactiveCocoa/Objective-C/RACTuple.h
[RACUnit]: ../../ReactiveCocoa/Objective-C/RACUnit.h
[README]: README.md
[seq]: http://clojure.org/sequences
This diff is collapsed.
# Objective-C Bridging
While ReactiveCocoa 3.0 introduces an entirely new design, it also aims for maximum compatibility with RAC 2, to ease the pain of migration. To interoperate with RAC 2’s Objective-C APIs, RAC 3 offers bridging functions that can convert Objective-C types to Swift types and vice-versa.
Because the APIs are based on fundamentally different designs, the conversion is not always one-to-one; however, every attempt has been made to faithfully translate the concepts between the two APIs (and languages).
The bridged types include:
1. [`RACSignal` and `SignalProducer` or `Signal`](#racsignal-and-signalproducer-or-signal)
1. [`RACCommand` and `Action`](#raccommand-and-action)
1. [`RACScheduler` and `SchedulerType`](#racscheduler-and-schedulertype)
1. [`RACDisposable` and `Disposable`](#racdisposable-and-disposable)
For the complete bridging API, including documentation, see [`ObjectiveCBridging.swift`][ObjectiveCBridging]. To learn more about how to migrate between ReactiveCocoa 2 and 3, see the [CHANGELOG][].
## `RACSignal` and `SignalProducer` or `Signal`
In RAC 3, “cold” signals are represented by the `SignalProducer` type, and “hot” signals are represented by the `Signal` type.
“Cold” `RACSignal`s can be converted into `SignalProducer`s using the new `toSignalProducer` method:
```swift
extension RACSignal {
func toSignalProducer() -> SignalProducer<AnyObject?, NSError>
}
```
“Hot” `RACSignal`s cannot be directly converted into `Signal`s, because _any_ `RACSignal` subscription could potentially involve side effects. To obtain a `Signal`, use `RACSignal.toSignalProducer` followed by `SignalProducer.start`, which will make those potential side effects explicit.
For the other direction, use the `toRACSignal()` function.
When called with a `SignalProducer`, these functions will create a `RACSignal` to `start()` the producer once for each subscription:
```swift
func toRACSignal<T: AnyObject, E>(producer: SignalProducer<T, E>) -> RACSignal
func toRACSignal<T: AnyObject, E>(producer: SignalProducer<T?, E>) -> RACSignal
```
When called with a `Signal`, these functions will create a `RACSignal` that simply observes it:
```swift
func toRACSignal<T: AnyObject, E>(signal: Signal<T, E>) -> RACSignal
func toRACSignal<T: AnyObject, E>(signal: Signal<T?, E>) -> RACSignal
```
## `RACCommand` and `Action`
To convert `RACCommand`s into the new `Action` type, use the `toAction()` extension method:
```swift
extension RACCommand {
func toAction() -> Action<AnyObject?, AnyObject?, NSError>
}
```
To convert `Action`s into `RACCommand`s, use the `toRACCommand()` function:
```swift
func toRACCommand<Output: AnyObject, E>(action: Action<AnyObject, Output, E>) -> RACCommand
func toRACCommand<Output: AnyObject, E>(action: Action<AnyObject?, Output, E>) -> RACCommand
```
**NOTE:** The `executing` properties of actions and commands are not synchronized across the API bridge. To ensure consistency, only observe the `executing` property from the base object (the one passed _into_ the bridge, not retrieved from it), so updates occur no matter which object is used for execution.
## `RACScheduler` and `SchedulerType`
Any `RACScheduler` instance is automatically a `DateSchedulerType` (and therefore a `SchedulerType`), and can be passed directly into any function or method that expects one.
Some (but not all) `SchedulerType`s from RAC 3 can be converted into `RACScheduler` instances, using the `toRACScheduler()` method:
```swift
extension ImmediateScheduler {
func toRACScheduler() -> RACScheduler
}
extension UIScheduler {
func toRACScheduler() -> RACScheduler
}
extension QueueScheduler {
func toRACScheduler() -> RACScheduler
}
```
## `RACDisposable` and `Disposable`
Any `RACDisposable` instance is automatically a `Disposable`, and can be used directly anywhere a type conforming to `Disposable` is expected.
Although there is no direct conversion from `Disposable` into `RACDisposable`, it is easy to do manually:
```swift
let swiftDisposable: Disposable
let objcDisposable = RACDisposable {
swiftDisposable.dispose()
}
```
[CHANGELOG]: ../CHANGELOG.md
[ObjectiveCBridging]: ../ReactiveCocoa/Swift/ObjectiveCBridging.swift
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment