Merge branch 'master' into 2.0-development

This commit is contained in:
Justin Spahr-Summers
2013-07-03 10:54:59 -07:00
3 changed files with 146 additions and 40 deletions

View File

@@ -6,6 +6,11 @@ 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)
@@ -24,6 +29,85 @@ known as [stream][Streams] operators.
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
[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.
@@ -269,6 +353,7 @@ RACSignal *switched = [signalOfSignals switchToLatest];
[letters sendNext:@"D"];
```
[Connections]: FrameworkOverview.md#connections
[RACSequence]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
[RACSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
[RACSignal+Operations]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
@@ -276,3 +361,4 @@ RACSignal *switched = [signalOfSignals switchToLatest];
[Sequences]: FrameworkOverview.md#sequences
[Signals]: FrameworkOverview.md#signals
[Streams]: FrameworkOverview.md#streams
[Subscription]: FrameworkOverview.md#subscription

View File

@@ -8,51 +8,46 @@ If you're already familiar with functional reactive programming or know the basi
premise of ReactiveCocoa, check out the [Documentation][] folder for a framework
overview and more in-depth information about how it all works in practice.
**Table of Contents**
## New to ReactiveCocoa?
1. [Functional reactive programming](#functional-reactive-programming)
1. [FRP with ReactiveCocoa](#frp-with-reactivecocoa)
ReactiveCocoa is documented like crazy, and there's a wealth of introductory
material available to explain what RAC is and how you can use it.
If you want to learn more, we recommend these resources, roughly in order:
1. [Introduction](#introduction)
1. [When to use ReactiveCocoa](#when-to-use-reactivecocoa)
1. [Importing ReactiveCocoa](#importing-reactivecocoa)
1. [More Info](#more-info)
1. [Framework Overview][]
1. [Basic Operators][]
1. [Header documentation](ReactiveCocoaFramework/ReactiveCocoa)
1. Previously answered [Stack Overflow](https://github.com/ReactiveCocoa/ReactiveCocoa/wiki)
questions and [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?labels=question&state=closed)
1. The rest of the [Documentation][] folder
## Functional reactive programming
## Introduction
Functional Reactive Programming (FRP) is a programming paradigm for writing
software that reacts to change.
ReactiveCocoa is an implementation of [functional reactive
programming](http://blog.maybeapps.com/post/42894317939/input-and-output).
Rather than using mutable variables which are replaced and modified in-place,
RAC provides signals (represented by `RACSignal`) that capture present and
future values.
FRP is built on the abstraction of values over time. Rather than capturing
a value at a particular time, FRP provides signals that capture the past,
present, and future value. These signals can be reasoned about, chained,
composed, and reacted to.
By chaining, combining, and reacting to signals, software can be written
declaratively, without the need for code that continually observes and updates
values.
By combining signals, software can be written declaratively, without the need
for code that continually observes and updates values. A text field can be
directly set to always show the current timestamp, for example, instead of using
additional code that watches the clock and updates the text field every second.
For example, a text field can be bound to the latest time, even as it changes,
instead of using additional code that watches the clock and updates the
text field every second. It works much like KVO, but with blocks instead of
overriding `-observeValueForKeyPath:ofObject:change:context:`.
Signals can also represent asynchronous operations, much like [futures and
promises][]. This greatly simplifies asynchronous software, including networking
code.
One of the major advantages of FRP is that it provides a single, unified
approach to dealing with different types of reactive, asynchronous behaviors.
Here are some resources for learning more about FRP:
* [What is FRP? - Elm Language](http://elm-lang.org/learn/What-is-FRP.elm)
* [What is Functional Reactive Programming - Stack Overflow](http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631)
[Josh Abernathy](https://github.com/joshaber) also has [a blog post about FRP](http://blog.maybeapps.com/post/42894317939/input-and-output).
## FRP with ReactiveCocoa
Signals in ReactiveCocoa (RAC) are represented using `RACSignal`. Signals are
streams of values that can be observed and transformed.
Applications that are built with RAC use signals to propagate changes. It works
much like KVO, but with blocks instead of overriding
`-observeValueForKeyPath:ofObject:change:context:`.
One of the major advantages of RAC is that it provides a single, unified
approach to dealing with asynchronous behaviors, including delegate methods,
callback blocks, target-action mechanisms, notifications, and KVO.
Here's a simple example:
@@ -83,10 +78,9 @@ But unlike KVO notifications, signals can be chained together and operated on:
}];
```
Signals can also be used to derive state, which is a key component of FRP.
Instead of observing properties and setting other properties in response to the
new values, RAC makes it possible to express properties in terms of signals and
operations:
Signals can also be used to derive state. Instead of observing properties and
setting other properties in response to the new values, RAC makes it possible to
express properties in terms of signals and operations:
```objc
// Create a one-way binding so that self.createEnabled will be
@@ -515,6 +509,13 @@ out there:
* [101 Rx Samples](http://rxwiki.wikidot.com/101samples)
* [Programming Reactive Extensions and LINQ](http://www.amazon.com/Programming-Reactive-Extensions-Jesse-Liberty/dp/1430237473)
RAC and Rx are both implementations of functional reactive programming. Here are
some more resources for learning about FRP:
* [What is FRP? - Elm Language](http://elm-lang.org/learn/What-is-FRP.elm)
* [What is Functional Reactive Programming - Stack Overflow](http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631)
[Basic Operators]: Documentation/BasicOperators.md
[Documentation]: Documentation
[Framework Overview]: Documentation/FrameworkOverview.md
[Functional Reactive Programming]: http://en.wikipedia.org/wiki/Functional_reactive_programming

View File

@@ -221,8 +221,10 @@ describe(@"subscribing", ^{
});
expect(currentScheduler).willNot.beNil();
});
});
it(@"should support -takeUntil:", ^{
describe(@"-takeUntil:", ^{
it(@"should support value as trigger", ^{
__block BOOL shouldBeGettingItems = YES;
RACSubject *subject = [RACSubject subject];
RACSubject *cutOffSubject = [RACSubject subject];
@@ -240,7 +242,7 @@ describe(@"subscribing", ^{
[subject sendNext:@"test 3"];
});
it(@"should support -takeUntil: with completion as trigger", ^{
it(@"should support completion as trigger", ^{
__block BOOL shouldBeGettingItems = YES;
RACSubject *subject = [RACSubject subject];
RACSubject *cutOffSubject = [RACSubject subject];
@@ -253,6 +255,23 @@ describe(@"subscribing", ^{
shouldBeGettingItems = NO;
[subject sendNext:@"should not go through"];
});
it(@"should squelch any values sent immediately upon subscription", ^{
RACSignal *valueSignal = [RACSignal return:RACUnit.defaultUnit];
RACSignal *cutOffSignal = [RACSignal empty];
__block BOOL gotNext = NO;
__block BOOL completed = NO;
[[valueSignal takeUntil:cutOffSignal] subscribeNext:^(id _) {
gotNext = YES;
} completed:^{
completed = YES;
}];
expect(gotNext).to.beFalsy();
expect(completed).to.beTruthy();
});
});
describe(@"disposal", ^{