Project import
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..bfcbe8a
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,417 @@
+# 2.0
+
+This release of ReactiveCocoa contains major breaking changes that we were
+unable to make after freezing the 1.0 API. All changes are focused on
+eliminating bad code and bad usage patterns, reducing confusion, and increasing
+flexibility in the framework.
+
+For a complete list of changes in ReactiveCocoa 2.0, see [the
+milestone](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?milestone=3&state=closed).
+
+**[Breaking changes](#breaking-changes)**
+
+ 1. [Simplified and safer KVO](#simplified-and-safer-kvo)
+ 1. [Safer commands with less state](#safer-commands-with-less-state)
+ 1. [Fallback nil value for RAC macro](#fallback-nil-values-for-rac-macro)
+ 1. [Explicit schedulers for time-based operators](#explicit-schedulers-for-time-based-operators)
+ 1. [More powerful selector signals](#more-powerful-selector-signals)
+ 1. [Simpler two-way bindings](#simpler-two-way-bindings)
+ 1. [Better bindings for AppKit](#better-bindings-for-appkit)
+ 1. [More obvious sequencing operator](#more-obvious-sequencing-operator)
+ 1. [Renamed signal binding method](#renamed-signal-binding-method)
+ 1. [Consistent selector lifting](#consistent-selector-lifting)
+ 1. [Renamed scheduled signal constructors](#renamed-scheduled-signal-constructors)
+ 1. [Notification immediately before object deallocation](#notification-immediately-before-object-deallocation)
+ 1. [Extensible queue-based schedulers](#extensible-queue-based-schedulers)
+ 1. [GCD time values replaced with NSDate](#gcd-time-values-replaced-with-nsdate)
+ 1. [Windows and numbered buffers removed](#windows-and-numbered-buffers-removed)
+ 1. [NSTask extension removed](#nstask-extension-removed)
+ 1. [RACSubscriber class now private](#racsubscriber-class-now-private)
+
+**[Additions and improvements](#additions-and-improvements)**
+
+ 1. [Commands for UIButton](#commands-for-uibutton)
+ 1. [Signal for UIActionSheet button clicks](#signal-for-uiactionsheet-button-clicks)
+ 1. [Better documentation for asynchronous backtraces](#better-documentation-for-asynchronous-backtraces)
+ 1. [Fixed libextobjc duplicated symbols](#fixed-libextobjc-duplicated-symbols)
+ 1. [Bindings for UIKit classes](#bindings-for-uikit-classes)
+ 1. [Signal subscription side effects](#signal-subscription-side-effects)
+ 1. [Test scheduler](#test-scheduler)
+
+## Breaking changes
+
+### Simplified and safer KVO
+
+`RACAble` and `RACAbleWithStart` have been replaced with a single
+[RACObserve](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/601) macro.
+`RACObserve` always starts with the current value of the property, and will
+[notice the
+deallocation](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/678) of `weak`
+properties (unlike vanilla KVO).
+
+Unlike the previous macros, which only required one argument for key paths on
+`self`, `RACObserve` always requires two arguments.
+
+**To update:**
+
+ * Replace uses of `RACAbleWithStart(self.key)` with `RACObserve(self, key)`.
+ * Replace uses of `RACAble(self.key)` with `[RACObserve(self, key) skip:1]` (if
+ skipping the starting value is necessary).
+
+### Safer commands with less state
+
+`RACCommand` has been [completely
+refactored](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/733). It is no
+longer a `RACSignal`, and the behavior of `-addSignalBlock:` has been moved to
+the initializer, making the class almost entirely immutable.
+
+Reflecting the most common use case, KVO-notifying properties have been changed
+into signals instead. During the change, `canExecute` was also renamed to
+`enabled`, which should make its purpose more obvious for binding to the UI.
+
+In addition, changes to `executing`, `errors`, and `enabled` are now guaranteed
+to fire on the main thread, so UI observers no longer [run in the background
+unexpectedly](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/533).
+
+All together, these improvements should make `RACCommand` more composable and
+less imperative, so it fits into the framework better.
+
+**To update:**
+
+ * Move execution logic from `-addSignalBlock:` or `-subscribeNext:` into the
+ `signalBlock` passed to the initializer.
+ * Instead of subscribing to the result of `-addSignalBlock:`, subscribe to
+ `executionSignals` or the result of `-execute:` instead.
+ * Replace uses of `RACAbleWithStart(command, executing)` with
+ `command.executing`.
+ * Replace uses of `RACAbleWithStart(command, canExecute)` with
+ `command.enabled`.
+ * Remove uses of `deliverOn:RACScheduler.mainThreadScheduler` on
+ `RACCommand` properties, as they are now unnecessary.
+
+### Fallback nil values for RAC macro
+
+The `RAC` macro now [always
+requires](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/670) two or three
+arguments:
+
+ 1. The object to bind to.
+ 1. The key path to set when new values are sent.
+ 1. (Optional) A value to set when `nil` is sent on the signal.
+
+This is necessary to avoid a `-setNilValueForKey:` exception when observing
+a primitive property _through_ an intermediate object which gets set to `nil`.
+
+This is not actually a change in key-value observing behavior — it's a caveat
+with KVO regardless — but `RACObserve` makes it more prominent, because the
+deallocation of `weak` properties will be considered a change to `nil`.
+
+**To update:**
+
+ * Replace uses of `RAC(self.objectProperty)` with `RAC(self, objectProperty)`.
+ * When binding a signal that might send nil (like a key path observation) to
+ a primitive property, provide a default value: `RAC(self, integerProperty, @5)`
+
+### Explicit schedulers for time-based operators
+
+`-bufferWithTime:`, `+interval:`, and `-timeout:` have been unintuitive and
+error-prone because of their implicit use of a background scheduler.
+
+All of the aforementioned methods now require [a scheduler
+argument](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/588), so that it's
+clear how events should be delivered.
+
+**To update:**
+
+ * To match the previous behavior exactly, pass in `[RACScheduler scheduler]`.
+ Note that this creates a _new_ background scheduler for events to arrive upon.
+ * If you were already using `-deliverOn:` to force one of the above operators
+ to deliver onto a specific scheduler, you can eliminate that hop and pass the
+ scheduler into the operator directly.
+
+### More powerful selector signals
+
+`-rac_signalForSelector:` has been [completely
+refactored](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/590) to support
+listening for invocations of existing methods, new and existing methods with
+multiple arguments, and existing methods with return values.
+
+`+rac_signalForSelector:` (the class method variant) was removed, because
+swizzling class methods is dangerous and difficult to do correctly.
+
+**To update:**
+
+ * Most existing uses of `-rac_signalForSelector:` shouldn't require
+ any changes. However, the `super` implementation (if available) of any targeted selector will
+ now be invoked, where it wasn't previously. Verify that existing uses can
+ handle this case.
+ * Replace uses of `+rac_signalForSelector:` by implementing the class method
+ and sending arguments onto a `RACSubject` instead.
+
+### Simpler two-way bindings
+
+`RACPropertySubject` and `RACBinding` have been
+[replaced](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/695) with
+`RACChannel` and `RACChannelTerminal`. Similarly, `RACObservablePropertySubject`
+has been replaced with `RACKVOChannel`.
+
+In addition to slightly better terminology and more obvious usage, channels only
+offer two-way bindings by default, which is a simplification over the previous N-way binding
+interface.
+
+Because of the sweeping conceptual changes, the old APIs have been completely
+removed without deprecation.
+
+**To update:**
+
+ * Instead of creating a `RACPropertySubject`, create a `RACChannel`. Replace
+ N-way property subjects (where N is greater than 2) with multiple
+ `RACChannel`s.
+ * Instead of creating a `RACObservablePropertySubject`, create
+ a `RACKVOChannel` or use the `RACChannelTo` macro.
+ * Replace uses of `RACBinding` with `RACChannelTerminal`.
+ * Replace uses of `RACBind(self.objectProperty)` with `RACChannelTo(self,
+ objectProperty)`. Add a default value for primitive properties:
+ `RACChannelTo(self, integerProperty, @5)`
+ * Replace uses of `-bindTo:` with the explicit subscription of two endpoints:
+
+```objc
+[binding1.followingEndpoint subscribe:binding2.leadingEndpoint];
+[[binding2.leadingEndpoint skip:1] subscribe:binding1.followingEndpoint];
+```
+
+### Better bindings for AppKit
+
+`-rac_bind:toObject:withKeyPath:` and related methods have been
+[replaced](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/638) with
+`-rac_channelToBinding:options:`, which returns a `RACChannelTerminal` that can be used as
+a two-way binding or a one-way signal.
+
+**To update:**
+
+ * If possible, refactor code to use the new `RACChannel` interface. This
+ bridges Cocoa Bindings with the full power of ReactiveCocoa.
+ * For a direct conversion, use `-bind:toObject:withKeyPath:options:` with the
+ following options:
+ 1. `@{ NSContinuouslyUpdatesValueBindingOption: @YES }` for `-rac_bind:toObject:withKeyPath:`
+ 1. `@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSNullPlaceholderBindingOption: nilValue }` for `-rac_bind:toObject:withKeyPath:nilValue:`
+ 1. `@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSValueTransformerBindingOption: valueTransformer }` for `-rac_bind:toObject:withKeyPath:transform:`
+ 1. `@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSValueTransformerBindingOption: NSNegateBooleanTransformerName }` for `-rac_bind:toObject:withNegatedKeyPath:`
+
+### More obvious sequencing operator
+
+To make the sequencing and transformation operators less confusing,
+`-sequenceMany:` has been removed, and `-sequenceNext:` has been
+[renamed](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/635) to `-then:`.
+
+**To update:**
+
+ * Replace uses of `-sequenceMany:` with `-flattenMap:` and a block that doesn't
+ use its argument.
+ * Replace uses of `-sequenceNext:` with `-then:`.
+
+### Renamed signal binding method
+
+`-toProperty:onObject:` and `-[NSObject rac_deriveProperty:from:]` have been
+[combined](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/617) into a new
+`-[RACSignal setKeyPath:onObject:nilValue:]` method.
+
+The `nilValue` parameter was added in parallel with the
+[RAC](#fallback-nil-values-for-rac-macro) macro, but the semantics are otherwise
+identical.
+
+**To update:**
+
+ * Replace `-toProperty:onObject:` and `-rac_deriveProperty:from:` with
+ `-setKeyPath:onObject:`.
+ * When binding a signal that might send nil (like a key path observation) to
+ a primitive property, provide a default value: `[signal setKeyPath:@"integerProperty" onObject:self nilValue:@5]`
+
+### Consistent selector lifting
+
+In the interests of [parametricity](http://en.wikipedia.org/wiki/Parametricity),
+`-rac_liftSelector:withObjects:` has been
+[replaced](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/609) with
+`-rac_liftSelector:withSignals:`, which requires all arguments to be signals.
+This makes usage more consistent.
+
+`-rac_liftBlock:withArguments:` has been removed, because it's redundant with
+`RACSignal` operators.
+
+The `-rac_lift` proxy has also been removed, because there's no way to [make it
+consistent](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/520) in the
+same way.
+
+**To update:**
+
+ * Wrap non-signal arguments with `+[RACSignal return:]` and add a nil
+ terminator.
+ * Replace block lifting with `+combineLatest:reduce:`.
+ * Replace uses of `-rac_lift` with `-rac_liftSelector:withSignals:`.
+
+### Renamed scheduled signal constructors
+
+`+start:`, `+startWithScheduler:block`, and `+startWithScheduler:subjectBlock:`
+have been combined into a single
+[+startEagerlyWithScheduler:block:](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/536)
+constructor.
+
+The key improvements here are a more intuitive name, a required `RACScheduler`
+to make it clear where the block is executed, and use of `<RACSubscriber>`
+instead of `RACSubject` to make it more obvious how to use the block argument.
+
+**To update:**
+
+ * Use `[RACScheduler scheduler]` to match the previous implicit scheduling
+ behavior of `+start:`.
+ * Refactor blocks that return values and set `success`/`error`, to send events
+ to the given `<RACSubscriber>` instead.
+
+### Notification immediately before object deallocation
+
+`-rac_didDeallocSignal` has been
+[removed](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/585) in favor of
+[-rac_willDeallocSignal](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/580),
+because most teardown should happen _before_ the object becomes invalid.
+
+`-rac_addDeallocDisposable:` has also been
+[removed](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/586) in favor of
+using the object's `rac_deallocDisposable` directly.
+
+**To update:**
+
+ * Replace uses of `-rac_didDeallocSignal` with `rac_willDeallocSignal`.
+ * Replace uses of `-rac_addDeallocDisposable:` by invoking `-addDisposable:` on
+ the object's `rac_deallocDisposable` instead.
+
+### Extensible queue-based schedulers
+
+`RACQueueScheduler` has been [exposed as a public
+class](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/561), so consumers can create
+their own scheduler implementations using GCD queues.
+
+The
+[RACTargetQueueScheduler](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/561)
+subclass replaces the `+schedulerWithQueue:name:` method.
+
+**To update:**
+
+Replace uses of `+schedulerWithQueue:name:` with `-[RACTargetQueueScheduler initWithName:targetQueue:]`.
+
+### GCD time values replaced with NSDate
+
+`NSDate` now [replaces](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/664)
+`dispatch_time_t` values in `RACScheduler`, because dates are easier to use, more
+convertible to other formats, and can be used to implement a [virtualized time
+scheduler](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/171).
+
+**To update:**
+
+Replace `dispatch_time_t` calculations with `NSDate`.
+
+### Windows and numbered buffers removed
+
+`-windowWithStart:close:` and `-buffer:` have been
+[removed](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/616) because
+they're not well-tested, and their functionality can be achieved with other
+operators.
+
+`-bufferWithTime:` is still supported.
+
+**To update:**
+
+ * Refactor uses of `-windowWithStart:close:` with different patterns.
+ * Replace uses of `-buffer:` with [take, collect, and
+ repeat](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/587).
+
+### NSTask extension removed
+
+`NSTask+RACSupport` has been
+[removed](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/556), because it
+was buggy and unsupported.
+
+**To update:**
+
+Use a vanilla `NSTask`, and send events onto `RACSubject`s instead.
+
+### RACSubscriber class now private
+
+The `RACSubscriber` class (not to be confused with the protocol) should never be
+used directly, so it has been
+[hidden](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/584).
+
+**To update:**
+
+Replace uses of `RACSubscriber` with `id<RACSubscriber>` or `RACSubject`.
+
+## Additions and improvements
+
+### Commands for UIButton
+
+`UIButton` now has a [rac_command
+property](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/558).
+
+Any command set will be executed when the button is tapped, and the button will
+be disabled whenever the command is unable to execute.
+
+### Signal for UIActionSheet button clicks
+
+`UIActionSheet` now has a [rac_buttonClickedSignal
+property](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/611), which will
+fire whenever one of the sheet's buttons is clicked.
+
+### Better documentation for asynchronous backtraces
+
+Documentation has been
+[added](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/680) to
+`RACBacktrace` explaining how to start collecting backtraces and print them out
+in the debugger.
+
+The `+captureBacktrace…` methods have been renamed to `+backtrace…`, and
+`+printBacktrace` has been removed in favor of using `po [RACBacktrace backtrace]`
+from the debugger.
+
+The `rac_` GCD functions which collect backtraces have also been exposed,
+primarily for use on iOS.
+
+### Fixed libextobjc duplicated symbols
+
+If [libextobjc](https://github.com/jspahrsummers/libextobjc) was used in
+a project that statically linked ReactiveCocoa, duplicate symbol errors could
+result.
+
+To avoid this issue, RAC now
+[renames](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/612) the
+libextobjc symbols that it uses.
+
+### Bindings for UIKit classes
+
+RACChannel interfaces have been [added](https://github.com/ReactiveCocoa/pull/686)
+to many UIKit classes, greatly simplifying glue code between your models and views.
+
+For example, assuming you want to bind a `person` model's `name` property:
+
+```objc
+UITextField *nameField = ...;
+RACChannelTerminal *nameTerminal = RACChannelTo(model, name);
+RACChannelTerminal *nameFieldTerminal = [nameField rac_newTextChannel];
+[nameTerminal subscribe:nameFieldTerminal];
+[nameFieldTerminal subscribe:nameTerminal];
+```
+
+You may also bind multiple controls to the same property, for example a UISlider for
+coarse editing and a UIStepper for fine-grained editing.
+
+### Signal subscription side effects
+
+`RACSignal` now has the
+[-initially:](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/685)
+operator, which executes a given block each time the signal is subscribed to.
+This is symmetric to `-finally:`.
+
+### Test scheduler
+
+`RACTestScheduler` is a [new kind](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/716) of scheduler that
+virtualizes time. Enqueued blocks can be stepped through at any pace, no matter
+how far in the future they're scheduled for, making it easy to test time-based
+behavior without actually waiting in unit tests.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9956451
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+We love that you're interested in contributing to this project!
+
+To make the process as painless as possible, we have just a couple of guidelines
+that should make life easier for everyone involved.
+
+## Prefer Pull Requests
+
+If you know exactly how to implement the feature being suggested or fix the bug
+being reported, please open a pull request instead of an issue. Pull requests are easier than
+patches or inline code blocks for discussing and merging the changes.
+
+If you can't make the change yourself, please open an issue after making sure
+that one isn't already logged.
+
+## Contributing Code
+
+Fork this repository, make it awesomer (preferably in a branch named for the
+topic), send a pull request!
+
+All code contributions should match our [coding
+conventions](https://github.com/github/objective-c-conventions).
+
+Thanks for contributing! :boom::camel:
diff --git a/Documentation/BasicOperators.md b/Documentation/BasicOperators.md
new file mode 100644
index 0000000..03a4ca0
--- /dev/null
+++ b/Documentation/BasicOperators.md
@@ -0,0 +1,364 @@
+# 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]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
+[RACSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
+[RACSignal+Operations]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
+[RACStream]: ../ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
+[Sequences]: FrameworkOverview.md#sequences
+[Signals]: FrameworkOverview.md#signals
+[Streams]: FrameworkOverview.md#streams
+[Subscription]: FrameworkOverview.md#subscription
diff --git a/Documentation/DesignGuidelines.md b/Documentation/DesignGuidelines.md
new file mode 100644
index 0000000..106e845
--- /dev/null
+++ b/Documentation/DesignGuidelines.md
@@ -0,0 +1,751 @@
+# Design Guidelines
+
+This document contains guidelines for projects that want to make use of
+ReactiveCocoa. The content here is heavily inspired by the [Rx Design
+Guidelines](http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx).
+
+This document assumes basic familiarity
+with the features of ReactiveCocoa. The [Framework Overview][] is a better
+resource for getting up to speed on the functionality provided by RAC.
+
+**[The RACSequence contract](#the-racsequence-contract)**
+
+ 1. [Evaluation occurs lazily by default](#evaluation-occurs-lazily-by-default)
+ 1. [Evaluation blocks the caller](#evaluation-blocks-the-caller)
+ 1. [Side effects occur only once](#side-effects-occur-only-once)
+
+**[The RACSignal contract](#the-racsignal-contract)**
+
+ 1. [Signal events are serialized](#signal-events-are-serialized)
+ 1. [Subscription will always occur on a scheduler](#subscription-will-always-occur-on-a-scheduler)
+ 1. [Errors are propagated immediately](#errors-are-propagated-immediately)
+ 1. [Side effects occur for each subscription](#side-effects-occur-for-each-subscription)
+ 1. [Subscriptions are automatically disposed upon completion or error](#subscriptions-are-automatically-disposed-upon-completion-or-error)
+ 1. [Disposal cancels in-progress work and cleans up resources](#disposal-cancels-in-progress-work-and-cleans-up-resources)
+
+**[Best practices](#best-practices)**
+
+ 1. [Use descriptive declarations for methods and properties that return a signal](#use-descriptive-declarations-for-methods-and-properties-that-return-a-signal)
+ 1. [Indent stream operations consistently](#indent-stream-operations-consistently)
+ 1. [Use the same type for all the values of a stream](#use-the-same-type-for-all-the-values-of-a-stream)
+ 1. [Avoid retaining streams for too long](#avoid-retaining-streams-for-too-long)
+ 1. [Process only as much of a stream as needed](#process-only-as-much-of-a-stream-as-needed)
+ 1. [Deliver signal events onto a known scheduler](#deliver-signal-events-onto-a-known-scheduler)
+ 1. [Switch schedulers in as few places as possible](#switch-schedulers-in-as-few-places-as-possible)
+ 1. [Make the side effects of a signal explicit](#make-the-side-effects-of-a-signal-explicit)
+ 1. [Share the side effects of a signal by multicasting](#share-the-side-effects-of-a-signal-by-multicasting)
+ 1. [Debug streams by giving them names](#debug-streams-by-giving-them-names)
+ 1. [Avoid explicit subscriptions and disposal](#avoid-explicit-subscriptions-and-disposal)
+ 1. [Avoid using subjects when possible](#avoid-using-subjects-when-possible)
+
+**[Implementing new operators](#implementing-new-operators)**
+
+ 1. [Prefer building on RACStream methods](#prefer-building-on-racstream-methods)
+ 1. [Compose existing operators when possible](#compose-existing-operators-when-possible)
+ 1. [Avoid introducing concurrency](#avoid-introducing-concurrency)
+ 1. [Cancel work and clean up all resources in a disposable](#cancel-work-and-clean-up-all-resources-in-a-disposable)
+ 1. [Do not block in an operator](#do-not-block-in-an-operator)
+ 1. [Avoid stack overflow from deep recursion](#avoid-stack-overflow-from-deep-recursion)
+
+## The RACSequence contract
+
+[RACSequence][] is a _pull-driven_ stream. Sequences behave similarly to
+built-in collections, but with a few unique twists.
+
+### Evaluation occurs lazily by default
+
+Sequences are evaluated lazily by default. For example, in this sequence:
+
+```objc
+NSArray *strings = @[ @"A", @"B", @"C" ];
+RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) {
+ return [str stringByAppendingString:@"_"];
+}];
+```
+
+… no string appending is actually performed until the values of the sequence are
+needed. Accessing `sequence.head` will perform the concatenation of `A_`,
+accessing `sequence.tail.head` will perform the concatenation of `B_`, and so
+on.
+
+This generally avoids performing unnecessary work (since values that are never
+used are never calculated), but means that sequence processing [should be
+limited only to what's actually
+needed](#process-only-as-much-of-a-stream-as-needed).
+
+Once evaluated, the values in a sequence are memoized and do not need to be
+recalculated. Accessing `sequence.head` multiple times will only do the work of
+one string concatenation.
+
+If lazy evaluation is undesirable – for instance, because limiting memory usage
+is more important than avoiding unnecessary work – the
+[eagerSequence][RACSequence] property can be used to force a sequence (and any
+sequences derived from it afterward) to evaluate eagerly.
+
+### Evaluation blocks the caller
+
+Regardless of whether a sequence is lazy or eager, evaluation of any part of
+a sequence will block the calling thread until completed. This is necessary
+because values must be synchronously retrieved from a sequence.
+
+If evaluating a sequence is expensive enough that it might block the thread for
+a significant amount of time, consider creating a signal with
+[-signalWithScheduler:][RACSequence] and using that instead.
+
+### Side effects occur only once
+
+When the block passed to a sequence operator involves side effects, it is
+important to realize that those side effects will only occur once per value
+– namely, when the value is evaluated:
+
+```objc
+NSArray *strings = @[ @"A", @"B", @"C" ];
+RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) {
+ NSLog(@"%@", str);
+ return [str stringByAppendingString:@"_"];
+}];
+
+// Logs "A" during this call.
+NSString *concatA = sequence.head;
+
+// Logs "B" during this call.
+NSString *concatB = sequence.tail.head;
+
+// Does not log anything.
+NSString *concatB2 = sequence.tail.head;
+
+RACSequence *derivedSequence = [sequence map:^(NSString *str) {
+ return [@"_" stringByAppendingString:str];
+}];
+
+// Still does not log anything, because "B_" was already evaluated, and the log
+// statement associated with it will never be re-executed.
+NSString *concatB3 = derivedSequence.tail.head;
+```
+
+## The RACSignal contract
+
+[RACSignal][] is a _push-driven_ stream with a focus on asynchronous event
+delivery through _subscriptions_. For more information about signals and
+subscriptions, see the [Framework Overview][].
+
+### Signal events are serialized
+
+A signal may choose to deliver its events on any thread. Consecutive events are
+even allowed to arrive on different threads or schedulers, unless explicitly
+[delivered onto a particular
+scheduler](#deliver-signal-events-onto-a-known-scheduler).
+
+However, RAC guarantees that no two signal events will ever arrive concurrently.
+While an event is being processed, no other events will be delivered. The
+senders of any other events will be forced to wait until the current event has
+been handled.
+
+Most notably, this means that the blocks passed to
+[-subscribeNext:error:completed:][RACSignal] do not need to be synchronized with
+respect to each other, because they will never be invoked simultaneously.
+
+### Subscription will always occur on a scheduler
+
+To ensure consistent behavior for the `+createSignal:` and `-subscribe:`
+methods, each [RACSignal][] subscription is guaranteed to take place on
+a valid [RACScheduler][].
+
+If the subscriber's thread already has a [+currentScheduler][RACScheduler],
+scheduling takes place immediately; otherwise, scheduling occurs as soon as
+possible on a background scheduler. Note that the main thread is always
+associated with the [+mainThreadScheduler][RACScheduler], so subscription will
+always be immediate there.
+
+See the documentation for [-subscribe:][RACSignal] for more information.
+
+### Errors are propagated immediately
+
+In RAC, `error` events have exception semantics. When an error is sent on
+a signal, it will be immediately forwarded to all dependent signals, causing the
+entire chain to terminate.
+
+[Operators][RACSignal+Operations] whose primary purpose is to change
+error-handling behavior – like `-catch:`, `-catchTo:`, or `-materialize` – are
+obviously not subject to this rule.
+
+### Side effects occur for each subscription
+
+Each new subscription to a [RACSignal][] will trigger its side effects. This
+means that any side effects will happen as many times as subscriptions to the
+signal itself.
+
+Consider this example:
+```objc
+__block int aNumber = 0;
+
+// Signal that will have the side effect of incrementing `aNumber` block
+// variable for each subscription before sending it.
+RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ aNumber++;
+ [subscriber sendNext:@(aNumber)];
+ [subscriber sendCompleted];
+ return nil;
+}];
+
+// This will print "subscriber one: 1"
+[aSignal subscribeNext:^(id x) {
+ NSLog(@"subscriber one: %@", x);
+}];
+
+// This will print "subscriber two: 2"
+[aSignal subscribeNext:^(id x) {
+ NSLog(@"subscriber two: %@", x);
+}];
+```
+
+Side effects are repeated for each subscription. The same applies to
+[stream][RACStream] and [signal][RACSignal+Operations] operators:
+
+```objc
+__block int missilesToLaunch = 0;
+
+// Signal that will have the side effect of changing `missilesToLaunch` on
+// subscription.
+RACSignal *processedSignal = [[RACSignal
+ return:@"missiles"]
+ map:^(id x) {
+ missilesToLaunch++;
+ return [NSString stringWithFormat:@"will launch %d %@", missilesToLaunch, x];
+ }];
+
+// This will print "First will launch 1 missiles"
+[processedSignal subscribeNext:^(id x) {
+ NSLog(@"First %@", x);
+}];
+
+// This will print "Second will launch 2 missiles"
+[processedSignal subscribeNext:^(id x) {
+ NSLog(@"Second %@", x);
+}];
+```
+
+To suppress this behavior and have multiple subscriptions to a signal execute
+its side effects only once, a signal can be
+[multicasted](#share-the-side-effects-of-a-signal-by-multicasting).
+
+Side effects can be insidious and produce problems that are difficult to
+diagnose. For this reason it is suggested to
+[make side effects explicit](#make-the-side-effects-of-a-signal-explicit) when
+possible.
+
+### Subscriptions are automatically disposed upon completion or error
+
+When a [subscriber][RACSubscriber] is sent a `completed` or `error` event, the
+associated subscription will automatically be disposed. This behavior usually
+eliminates the need to manually dispose of subscriptions.
+
+See the [Memory Management][] document for more information about signal
+lifetime.
+
+### Disposal cancels in-progress work and cleans up resources
+
+When a subscription is disposed, manually or automatically, any in-progress or
+outstanding work associated with that subscription is gracefully cancelled as
+soon as possible, and any resources associated with the subscription are cleaned
+up.
+
+Disposing of the subscription to a signal representing a file upload, for
+example, would cancel any in-flight network request, and free the file data from
+memory.
+
+## Best practices
+
+The following recommendations are intended to help keep RAC-based code
+predictable, understandable, and performant.
+
+They are, however, only guidelines. Use best judgement when determining whether
+to apply the recommendations here to a given piece of code.
+
+### Use descriptive declarations for methods and properties that return a signal
+
+When a method or property has a return type of [RACSignal][], it can be
+difficult to understand the signal's semantics at a glance.
+
+There are three key questions that can inform a declaration:
+
+ 1. Is the signal _hot_ (already activated by the time it's returned to the
+ caller) or _cold_ (activated when subscribed to)?
+ 1. Will the signal include zero, one, or more values?
+ 1. Does the signal have side effects?
+
+**Hot signals without side effects** should typically be properties instead of
+methods. The use of a property indicates that no initialization is needed before
+subscribing to the signal's events, and that additional subscribers will not
+change the semantics. Signal properties should usually be named after events
+(e.g., `textChanged`).
+
+**Cold signals without side effects** should be returned from methods that have
+noun-like names (e.g., `-currentText`). A method declaration indicates that the
+signal might not be kept around, hinting that work is performed at the time of
+subscription. If the signal sends multiple values, the noun should be pluralized
+(e.g., `-currentModels`).
+
+**Signals with side effects** should be returned from methods that have
+verb-like names (e.g., `-logIn`). The verb indicates that the method is not
+idempotent and that callers must be careful to call it only when the side
+effects are desired. If the signal will send one or more values, include a noun
+that describes them (e.g., `-loadConfiguration`, `-fetchLatestEvents`).
+
+### Indent stream operations consistently
+
+It's easy for stream-heavy code to become very dense and confusing if not
+properly formatted. Use consistent indentation to highlight where chains of
+streams begin and end.
+
+When invoking a single method upon a stream, no additional indentation is
+necessary (block arguments aside):
+
+```objc
+RACStream *result = [stream startWith:@0];
+
+RACStream *result2 = [stream map:^(NSNumber *value) {
+ return @(value.integerValue + 1);
+}];
+```
+
+When transforming the same stream multiple times, ensure that all of the
+steps are aligned. Complex operators like [+zip:reduce:][RACStream] or
+[+combineLatest:reduce:][RACSignal+Operations] may be split over multiple lines
+for readability:
+
+```objc
+RACStream *result = [[[RACStream
+ zip:@[ firstStream, secondStream ]
+ reduce:^(NSNumber *first, NSNumber *second) {
+ return @(first.integerValue + second.integerValue);
+ }]
+ filter:^ BOOL (NSNumber *value) {
+ return value.integerValue >= 0;
+ }]
+ map:^(NSNumber *value) {
+ return @(value.integerValue + 1);
+ }];
+```
+
+Of course, streams nested within block arguments should start at the natural
+indentation of the block:
+
+```objc
+[[signal
+ then:^{
+ @strongify(self);
+
+ return [[self
+ doSomethingElse]
+ catch:^(NSError *error) {
+ @strongify(self);
+ [self presentError:error];
+
+ return [RACSignal empty];
+ }];
+ }]
+ subscribeCompleted:^{
+ NSLog(@"All done.");
+ }];
+```
+
+### Use the same type for all the values of a stream
+
+[RACStream][] (and, by extension, [RACSignal][] and [RACSequence][]) allows
+streams to be composed of heterogenous objects, just like Cocoa collections do.
+However, using different object types within the same stream complicates the use
+of operators and
+puts an additional burden on any consumers of that stream, who must be careful to
+only invoke supported methods.
+
+Whenever possible, streams should only contain objects of the same type.
+
+### Avoid retaining streams for too long
+
+Retaining any [RACStream][] longer than it's needed will cause any dependencies
+to be retained as well, potentially keeping memory usage much higher than it
+would be otherwise.
+
+A [RACSequence][] should be retained only for as long as the `head` of the
+sequence is needed. If the head will no longer be used, retain the `tail` of the
+node instead of the node itself.
+
+See the [Memory Management][] guide for more information on object lifetime.
+
+### Process only as much of a stream as needed
+
+As well as [consuming additional
+memory](#avoid-retaining-streams-for-too-long), unnecessarily
+keeping a stream or [RACSignal][] subscription alive can result in increased CPU
+usage, as unnecessary work is performed for results that will never be used.
+
+If only a certain number of values are needed from a stream, the
+[-take:][RACStream] operator can be used to retrieve only that many values, and
+then automatically terminate the stream immediately thereafter.
+
+Operators like `-take:` and [-takeUntil:][RACSignal+Operations] automatically propagate cancellation
+up the stack as well. If nothing else needs the rest of the values, any
+dependencies will be terminated too, potentially saving a significant amount of
+work.
+
+### Deliver signal events onto a known scheduler
+
+When a signal is returned from a method, or combined with such a signal, it can
+be difficult to know which thread events will be delivered upon. Although
+events are [guaranteed to be serial](#signal-events-are-serialized), sometimes
+stronger guarantees are needed, like when performing UI updates (which must
+occur on the main thread).
+
+Whenever such a guarantee is important, the [-deliverOn:][RACSignal+Operations]
+operator should be used to force a signal's events to arrive on a specific
+[RACScheduler][].
+
+### Switch schedulers in as few places as possible
+
+Notwithstanding the above, events should only be delivered to a specific
+[scheduler][RACScheduler] when absolutely necessary. Switching schedulers can
+introduce unnecessary delays and cause an increase in CPU load.
+
+Generally, the use of [-deliverOn:][RACSignal+Operations] should be restricted
+to the end of a signal chain – e.g., before subscription, or before the values
+are bound to a property.
+
+### Make the side effects of a signal explicit
+
+As much as possible, [RACSignal][] side effects should be avoided, because
+subscribers may find the [behavior of side
+effects](#side-effects-occur-for-each-subscription) unexpected.
+
+However, because Cocoa is predominantly imperative, it is sometimes useful to
+perform side effects when signal events occur. Although most [RACStream][] and
+[RACSignal][RACSignal+Operations] operators accept arbitrary blocks (which can
+contain side effects), the use of `-doNext:`, `-doError:`, and `-doCompleted:`
+will make side effects more explicit and self-documenting:
+
+```objc
+NSMutableArray *nexts = [NSMutableArray array];
+__block NSError *receivedError = nil;
+__block BOOL success = NO;
+
+RACSignal *bookkeepingSignal = [[[valueSignal
+ doNext:^(id x) {
+ [nexts addObject:x];
+ }]
+ doError:^(NSError *error) {
+ receivedError = error;
+ }]
+ doCompleted:^{
+ success = YES;
+ }];
+
+RAC(self, value) = bookkeepingSignal;
+```
+
+### Share the side effects of a signal by multicasting
+
+[Side effects occur for each
+subscription](#side-effects-occur-for-each-subscription) by default, but there
+are certain situations where side effects should only occur once – for example,
+a network request typically should not be repeated when a new subscriber is
+added.
+
+The `-publish` and `-multicast:` operators of [RACSignal][RACSignal+Operations]
+allow a single subscription to be shared to any number of subscribers by using
+a [RACMulticastConnection][]:
+
+```objc
+// This signal starts a new request on each subscription.
+RACSignal *networkRequest = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ AFHTTPRequestOperation *operation = [client
+ HTTPRequestOperationWithRequest:request
+ success:^(AFHTTPRequestOperation *operation, id response) {
+ [subscriber sendNext:response];
+ [subscriber sendCompleted];
+ }
+ failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ [subscriber sendError:error];
+ }];
+
+ [client enqueueHTTPRequestOperation:operation];
+ return [RACDisposable disposableWithBlock:^{
+ [operation cancel];
+ }];
+}];
+
+// Starts a single request, no matter how many subscriptions `connection.signal`
+// gets. This is equivalent to the -replay operator, or similar to
+// +startEagerlyWithScheduler:block:.
+RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]];
+[connection connect];
+
+[connection.signal subscribeNext:^(id response) {
+ NSLog(@"subscriber one: %@", response);
+}];
+
+[connection.signal subscribeNext:^(id response) {
+ NSLog(@"subscriber two: %@", response);
+}];
+```
+
+### Debug streams by giving them names
+
+Every [RACStream][] has a `name` property to assist with debugging. A stream's
+`-description` includes its name, and all operators provided by RAC will
+automatically add to the name. This usually makes it possible to identify
+a stream from its default name alone.
+
+For example, this snippet:
+
+```objc
+RACSignal *signal = [[[RACObserve(self, username)
+ distinctUntilChanged]
+ take:3]
+ filter:^(NSString *newUsername) {
+ return [newUsername isEqualToString:@"joshaber"];
+ }];
+
+NSLog(@"%@", signal);
+```
+
+… would log a name similar to `[[[RACObserve(self, username)] -distinctUntilChanged]
+-take: 3] -filter:`.
+
+Names can also be manually applied by using [-setNameWithFormat:][RACStream].
+
+[RACSignal][] also offers `-logNext`, `-logError`,
+`-logCompleted`, and `-logAll` methods, which will automatically log signal
+events as they occur, and include the name of the signal in the messages. This
+can be used to conveniently inspect a signal in real-time.
+
+### Avoid explicit subscriptions and disposal
+
+Although [-subscribeNext:error:completed:][RACSignal] and its variants are the
+most basic way to process a signal, their use can complicate code by
+being less declarative, encouraging the use of side effects, and potentially
+duplicating built-in functionality.
+
+Likewise, explicit use of the [RACDisposable][] class can quickly lead to
+a rat's nest of resource management and cleanup code.
+
+There are almost always higher-level patterns that can be used instead of manual
+subscriptions and disposal:
+
+ * The [RAC()][RAC] or [RACChannelTo()][RACChannelTo] macros can be used to bind
+ a signal to a property, instead of performing manual updates when changes
+ occur.
+ * The [-rac_liftSelector:withSignals:][NSObject+RACLifting] method can be used
+ to automatically invoke a selector when one or more signals fire.
+ * Operators like [-takeUntil:][RACSignal+Operations] can be used to
+ automatically dispose of a subscription when an event occurs (like a "Cancel"
+ button being pressed in the UI).
+
+Generally, the use of built-in [stream][RACStream] and
+[signal][RACSignal+Operations] operators will lead to simpler and less
+error-prone code than replicating the same behaviors in a subscription callback.
+
+### Avoid using subjects when possible
+
+[Subjects][] are a powerful tool for bridging imperative code
+into the world of signals, but, as the "mutable variables" of RAC, they can
+quickly lead to complexity when overused.
+
+Since they can be manipulated from anywhere, at any time, subjects often break
+the linear flow of stream processing and make logic much harder to follow. They
+also don't support meaningful
+[disposal](#disposal-cancels-in-progress-work-and-cleans-up-resources), which
+can result in unnecessary work.
+
+Subjects can usually be replaced with other patterns from ReactiveCocoa:
+
+ * Instead of feeding initial values into a subject, consider generating the
+ values in a [+createSignal:][RACSignal] block instead.
+ * Instead of delivering intermediate results to a subject, try combining the
+ output of multiple signals with operators like
+ [+combineLatest:][RACSignal+Operations] or [+zip:][RACStream].
+ * Instead of using subjects to share results with multiple subscribers,
+ [multicast](#share-the-side-effects-of-a-signal-by-multicasting) a base
+ signal instead.
+ * Instead of implementing an action method which simply controls a subject, use
+ a [command][RACCommand] or
+ [-rac_signalForSelector:][NSObject+RACSelectorSignal] instead.
+
+When subjects _are_ necessary, they should almost always be the "base" input
+for a signal chain, not used in the middle of one.
+
+## Implementing new operators
+
+RAC provides a long list of built-in operators for [streams][RACStream] and
+[signals][RACSignal+Operations] that should cover most use cases; however, RAC
+is not a closed system. It's entirely valid to implement additional operators
+for specialized uses, or for consideration in ReactiveCocoa itself.
+
+Implementing a new operator requires a careful attention to detail and a focus
+on simplicity, to avoid introducing bugs into the calling code.
+
+These guidelines cover some of the common pitfalls and help preserve the
+expected API contracts.
+
+### Prefer building on RACStream methods
+
+[RACStream][] offers a simpler interface than [RACSequence][] and [RACSignal][],
+and all stream operators are automatically applicable to sequences and signals
+as well.
+
+For these reasons, new operators should be implemented using only [RACStream][]
+methods whenever possible. The minimal required methods of the class, including
+`-bind:`, `+zipWith:`, and `-concat:`, are quite powerful, and many tasks can
+be accomplished without needing anything else.
+
+If a new [RACSignal][] operator needs to handle `error` and `completed` events,
+consider using the [-materialize][RACSignal+Operations] method to bring the
+events into the stream. All of the events of a materialized signal can be
+manipulated by stream operators, which helps minimize the use of non-stream
+operators.
+
+### Compose existing operators when possible
+
+Considerable thought has been put into the operators provided by RAC, and they
+have been validated through automated tests and through their real world use in
+other projects. An operator that has been written from scratch may not be as
+robust, or might not handle a special case that the built-in operators are aware
+of.
+
+To minimize duplication and possible bugs, use the provided operators as much as
+possible in a custom operator implementation. Generally, there should be very
+little code written from scratch.
+
+### Avoid introducing concurrency
+
+Concurrency is an extremely common source of bugs in programming. To minimize
+the potential for deadlocks and race conditions, operators should not
+concurrently perform their work.
+
+Callers always have the ability to subscribe or deliver events on a specific
+[RACScheduler][], and RAC offers powerful ways to [parallelize
+work][Parallelizing Independent Work] without making operators unnecessarily
+complex.
+
+### Cancel work and clean up all resources in a disposable
+
+When implementing a signal with the [+createSignal:][RACSignal] method, the
+provided block is expected to return a [RACDisposable][]. This disposable
+should:
+
+ * As soon as it is convenient, gracefully cancel any in-progress work that was
+ started by the signal.
+ * Immediately dispose of any subscriptions to other signals, thus triggering
+ their cancellation and cleanup code as well.
+ * Release any memory or other resources that were allocated by the signal.
+
+This helps fulfill [the RACSignal
+contract](#disposal-cancels-in-progress-work-and-cleans-up-resources).
+
+### Do not block in an operator
+
+Stream operators should return a new stream more-or-less immediately. Any work
+that the operator needs to perform should be part of evaluating the new stream,
+_not_ part of the operator invocation itself.
+
+```objc
+// WRONG!
+- (RACSequence *)map:(id (^)(id))block {
+ RACSequence *result = [RACSequence empty];
+ for (id obj in self) {
+ id mappedObj = block(obj);
+ result = [result concat:[RACSequence return:mappedObj]];
+ }
+
+ return result;
+}
+
+// Right!
+- (RACSequence *)map:(id (^)(id))block {
+ return [self flattenMap:^(id obj) {
+ id mappedObj = block(obj);
+ return [RACSequence return:mappedObj];
+ }];
+}
+```
+
+This guideline can be safely ignored when the purpose of an operator is to
+synchronously retrieve one or more values from a stream (like
+[-first][RACSignal+Operations]).
+
+### Avoid stack overflow from deep recursion
+
+Any operator that might recurse indefinitely should use the
+`-scheduleRecursiveBlock:` method of [RACScheduler][]. This method will
+transform recursion into iteration instead, preventing a stack overflow.
+
+For example, this would be an incorrect implementation of
+[-repeat][RACSignal+Operations], due to its potential to overflow the call stack
+and cause a crash:
+
+```objc
+- (RACSignal *)repeat {
+ return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ __block void (^resubscribe)(void) = ^{
+ RACDisposable *disposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ resubscribe();
+ }];
+
+ [compoundDisposable addDisposable:disposable];
+ };
+
+ return compoundDisposable;
+ }];
+}
+```
+
+By contrast, this version will avoid a stack overflow:
+
+```objc
+- (RACSignal *)repeat {
+ return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ RACScheduler *scheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler];
+ RACDisposable *disposable = [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) {
+ RACDisposable *disposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ reschedule();
+ }];
+
+ [compoundDisposable addDisposable:disposable];
+ }];
+
+ [compoundDisposable addDisposable:disposable];
+ return compoundDisposable;
+ }];
+}
+```
+
+[Framework Overview]: FrameworkOverview.md
+[Memory Management]: MemoryManagement.md
+[NSObject+RACLifting]: ../ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.h
+[NSObject+RACSelectorSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h
+[RAC]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h
+[RACChannelTo]: ../ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.h
+[RACCommand]: ../ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h
+[RACDisposable]: ../ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h
+[RACEvent]: ../ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h
+[RACMulticastConnection]: ../ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h
+[RACObserve]: ../ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.h
+[RACScheduler]: ../ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h
+[RACSequence]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
+[RACSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
+[RACSignal+Operations]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
+[RACStream]: ../ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
+[RACSubscriber]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h
+[Subjects]: FrameworkOverview.md#subjects
+[Parallelizing Independent Work]: ../README.md#parallelizing-independent-work
diff --git a/Documentation/DifferencesFromRx.md b/Documentation/DifferencesFromRx.md
new file mode 100644
index 0000000..6e94e64
--- /dev/null
+++ b/Documentation/DifferencesFromRx.md
@@ -0,0 +1,59 @@
+# 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/ReactiveCocoaFramework/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/ReactiveCocoaFramework/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/ReactiveCocoaFramework/ReactiveCocoa/RACSignal%2BOperations.h)
+ category upon it.
+ * **[RACSequence](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/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;
+```
diff --git a/Documentation/FrameworkOverview.md b/Documentation/FrameworkOverview.md
new file mode 100644
index 0000000..174303d
--- /dev/null
+++ b/Documentation/FrameworkOverview.md
@@ -0,0 +1,216 @@
+# 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].
+
+## Asynchronous Backtraces
+
+Because RAC-based code often involves asynchronous work and queue-hopping, the
+framework supports [capturing asynchronous backtraces][RACBacktrace] to make debugging
+easier.
+
+On OS X, backtraces can be automatically captured from any code, including
+system libraries.
+
+On iOS, only queue hops from within RAC and your project will be captured (but
+the information is still valuable).
+
+[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]: http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Data-List.html
+[Memory Management]: MemoryManagement.md
+[monads]: http://en.wikipedia.org/wiki/Monad_(functional_programming)
+[Monoid]: http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Data-Monoid.html#t:Monoid
+[MonadZip]: http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Control-Monad-Zip.html#t:MonadZip
+[NSButton+RACCommandSupport]: ../ReactiveCocoaFramework/ReactiveCocoa/NSButton+RACCommandSupport.h
+[RACBacktrace]: ../ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.h
+[RACCommand]: ../ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h
+[RACDisposable]: ../ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h
+[RACEvent]: ../ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h
+[RACMulticastConnection]: ../ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h
+[RACReplaySubject]: ../ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.h
+[RACScheduler]: ../ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h
+[RACSequence]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
+[RACSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
+[RACSignal+Operations]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
+[RACStream]: ../ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
+[RACSubject]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSubject.h
+[RACSubscriber]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h
+[RACTuple]: ../ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h
+[RACUnit]: ../ReactiveCocoaFramework/ReactiveCocoa/RACUnit.h
+[README]: ../README.md
+[seq]: http://clojure.org/sequences
diff --git a/Documentation/MemoryManagement.md b/Documentation/MemoryManagement.md
new file mode 100644
index 0000000..e442b1f
--- /dev/null
+++ b/Documentation/MemoryManagement.md
@@ -0,0 +1,139 @@
+# Memory Management
+
+ReactiveCocoa's memory management is quite complex, but the end result is that
+**you don't need to retain signals in order to process them**.
+
+If the framework required you to retain every signal, it'd be much more unwieldy
+to use, especially for one-shot signals that are used like futures (e.g.,
+network requests). You'd have to save any long-lived signal into a property, and
+then also make sure to clear it out when you're done with it. Not fun.
+
+## Subscribers
+
+Before going any further, it's worth noting that
+`subscribeNext:error:completed:` (and all variants thereof) create an _implicit_
+subscriber using the given blocks. Any objects referenced from those blocks will
+therefore be retained as part of the subscription. Just like any other object,
+`self` won't be retained without a direct or indirect reference to it.
+
+## Finite or Short-Lived Signals
+
+The most important guideline to RAC memory management is that a **subscription
+is automatically terminated upon completion or error, and the subscriber
+removed**.
+
+For example, if you have some code like this in your view controller:
+
+```objc
+self.disposable = [signal subscribeCompleted:^{
+ doSomethingPossiblyInvolving(self);
+}];
+```
+
+… the memory management will look something like the following:
+
+```
+view controller -> RACDisposable -> RACSignal -> RACSubscriber -> view controller
+```
+
+However, the `RACSignal -> RACSubscriber` relationship is torn down as soon as
+`signal` finishes, breaking the retain cycle.
+
+**This is often all you need**, because the lifetime of the `RACSignal` in
+memory will naturally match the logical lifetime of the event stream.
+
+## Infinite Signals
+
+Infinite signals (or signals that live so long that they might as well be
+infinite), however, will never tear down naturally. This is where disposables
+shine.
+
+**Disposing of a subscription will remove the associated subscriber**, and just
+generally clean up any resources associated with that subscription. To that one
+subscriber, it's just as if the signal had completed or errored, except no final
+event is sent on the signal. All other subscribers will remain intact.
+
+However, as a general rule of thumb, if you have to manually manage
+a subscription's lifecycle, [there's probably a better way to do what you want][avoid-explicit-subscriptions-and-disposal].
+
+## Signals Derived from `self`
+
+There's still a bit of a tricky middle case here, though. Any time a signal's
+lifetime is tied to the calling scope, you'll have a much harder cycle to break.
+
+This commonly occurs when using `RACObserve()` on a key
+path that's relative to `self`, and then applying a block that needs to capture
+`self`.
+
+The easiest answer here is just to **capture `self` weakly**:
+
+```objc
+__weak id weakSelf = self;
+[RACObserve(self, username) subscribeNext:^(NSString *username) {
+ id strongSelf = weakSelf;
+ [strongSelf validateUsername];
+}];
+```
+
+Or, after importing the included
+[EXTScope.h](https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTScope.h)
+header:
+
+```objc
+@weakify(self);
+[RACObserve(self, username) subscribeNext:^(NSString *username) {
+ @strongify(self);
+ [self validateUsername];
+}];
+```
+
+*(Replace `__weak` or `@weakify` with `__unsafe_unretained` or `@unsafeify`,
+respectively, if the object doesn't support weak references.)*
+
+However, [there's probably a better pattern you could use instead][avoid-explicit-subscriptions-and-disposal]. For
+example, the above sample could perhaps be written like:
+
+```objc
+[self rac_liftSelector:@selector(validateUsername:) withSignals:RACObserve(self, username), nil];
+```
+
+or:
+
+```objc
+RACSignal *validated = [RACObserve(self, username) map:^(NSString *username) {
+ // Put validation logic here.
+ return @YES;
+}];
+```
+
+As with infinite signals, there are generally ways you can avoid referencing
+`self` (or any object) from blocks in a signal chain.
+
+----
+
+The above information is really all you should need in order to use
+ReactiveCocoa effectively. However, there's one more point to address, just for
+the technically curious or for anyone interested in contributing to RAC.
+
+The design goal of "no retaining necessary" begs the question: how do we know
+when a signal should be deallocated? What if it was just created, escaped an
+autorelease pool, and hasn't been retained yet?
+
+The real answer is _we don't_, BUT we can usually assume that the caller will
+retain the signal within the current run loop iteration if they want to keep it.
+
+Consequently:
+
+ 1. A created signal is automatically added to a global set of active signals.
+ 2. The signal will wait for a single pass of the main run loop, and then remove
+ itself from the active set _if it has no subscribers_. Unless the signal was
+ retained somehow, it would deallocate at this point.
+ 3. If something did subscribe in that run loop iteration, the signal stays in
+ the set.
+ 4. Later, when all the subscribers are gone, step 2 is triggered again.
+
+This could backfire if the run loop is spun recursively (like in a modal event
+loop on OS X), but it makes the life of the framework consumer much easier for
+most or all other cases.
+
+[avoid-explicit-subscriptions-and-disposal]: DesignGuidelines.md#avoid-explicit-subscriptions-and-disposal
diff --git a/Documentation/README.md b/Documentation/README.md
new file mode 100644
index 0000000..4e0d4ed
--- /dev/null
+++ b/Documentation/README.md
@@ -0,0 +1,2 @@
+This folder contains conceptual documentation and design guidelines that don't
+fit well on a single class or in any specific header file.
diff --git a/Instruments/Disposable Growth.tracetemplate b/Instruments/Disposable Growth.tracetemplate
new file mode 100644
index 0000000..24dc61c
--- /dev/null
+++ b/Instruments/Disposable Growth.tracetemplate
Binary files differ
diff --git a/Instruments/README.md b/Instruments/README.md
new file mode 100644
index 0000000..e2f1d4a
--- /dev/null
+++ b/Instruments/README.md
@@ -0,0 +1,14 @@
+This folder contains Instruments templates to make it easier to debug
+code using ReactiveCocoa.
+
+To get started with a template, simply double-click it.
+
+### Signal Names
+
+The `name` property of `RACSignal` requires that the `RAC_DEBUG_SIGNAL_NAMES`
+environment variable be set, which means that you won't have access to
+meaningful names in Instruments by default.
+
+To add signal names, open your application's scheme in Xcode, select the Profile
+action, and add `RAC_DEBUG_SIGNAL_NAMES` with a value of `1` to the list of
+environment variables.
diff --git a/Instruments/Signal Events.tracetemplate b/Instruments/Signal Events.tracetemplate
new file mode 100644
index 0000000..4c90d88
--- /dev/null
+++ b/Instruments/Signal Events.tracetemplate
Binary files differ
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..56cda91
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,19 @@
+**Copyright (c) 2012 - 2014, GitHub, Inc.**
+**All rights reserved.**
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c919c15
--- /dev/null
+++ b/README.md
@@ -0,0 +1,562 @@
+# ReactiveCocoa
+
+ReactiveCocoa (RAC) is an Objective-C framework for [Functional Reactive
+Programming][]. It provides APIs for **composing and transforming streams of
+values**.
+
+If you're already familiar with functional reactive programming or know the basic
+premise of ReactiveCocoa, check out the [Documentation][] folder for a framework
+overview and more in-depth information about how it all works in practice.
+
+## New to 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. [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
+ 1. [Functional Reactive Programming on iOS](https://leanpub.com/iosfrp/)
+ (eBook)
+
+If you have any further questions, please feel free to [file an issue](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/new).
+
+## Introduction
+
+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.
+
+By chaining, combining, and reacting to signals, software can be written
+declaratively, without the need for code that continually observes and updates
+values.
+
+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 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:
+
+```objc
+// When self.username changes, log the new name to the console.
+//
+// RACObserve(self, username) creates a new RACSignal that sends the current
+// value of self.username, then the new value whenever it changes.
+// -subscribeNext: will execute the block whenever the signal sends a value.
+[RACObserve(self, username) subscribeNext:^(NSString *newName) {
+ NSLog(@"%@", newName);
+}];
+```
+
+But unlike KVO notifications, signals can be chained together and operated on:
+
+```objc
+// Only log names that start with "j".
+//
+// -filter returns a new RACSignal that only sends a new value when its block
+// returns YES.
+[[RACObserve(self, username)
+ filter:^(NSString *newName) {
+ return [newName hasPrefix:@"j"];
+ }]
+ subscribeNext:^(NSString *newName) {
+ NSLog(@"%@", newName);
+ }];
+```
+
+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
+// true whenever self.password and self.passwordConfirmation
+// are equal.
+//
+// RAC() is a macro that makes the binding look nicer.
+//
+// +combineLatest:reduce: takes an array of signals, executes the block with the
+// latest value from each signal whenever any of them changes, and returns a new
+// RACSignal that sends the return value of that block as values.
+RAC(self, createEnabled) = [RACSignal
+ combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ]
+ reduce:^(NSString *password, NSString *passwordConfirm) {
+ return @([passwordConfirm isEqualToString:password]);
+ }];
+```
+
+Signals can be built on any stream of values over time, not just KVO. For
+example, they can also represent button presses:
+
+```objc
+// Log a message whenever the button is pressed.
+//
+// RACCommand creates signals to represent UI actions. Each signal can
+// represent a button press, for example, and have additional work associated
+// with it.
+//
+// -rac_command is an addition to NSButton. The button will send itself on that
+// command whenever it's pressed.
+self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ NSLog(@"button was pressed!");
+ return [RACSignal empty];
+}]
+```
+
+Or asynchronous network operations:
+
+```objc
+// Hook up a "Log in" button to log in over the network.
+//
+// This block will be run whenever the login command is executed, starting
+// the login process.
+self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^(id sender) {
+ // The hypothetical -logIn method returns a signal that sends a value when
+ // the network request finishes.
+ return [client logIn];
+}];
+
+// -executionSignals returns a signal that includes the signals returned from
+// the above block, one for each time the command is executed.
+[self.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) {
+ // Log a message whenever we log in successfully.
+ [loginSignal subscribeCompleted:^ {
+ NSLog(@"Logged in successfully!");
+ }];
+}];
+
+// Execute the login command when the button is pressed.
+self.loginButton.rac_command = self.loginCommand;
+```
+
+Signals can also represent timers, other UI events, or anything else that
+changes over time.
+
+Using signals for asynchronous operations makes it possible to build up more
+complex behavior by chaining and transforming those signals. Work can easily be
+trigged after a group of operations completes:
+
+```objc
+// Perform 2 network operations and log a message to the console when they are
+// both completed.
+//
+// +merge: takes an array of signals and returns a new RACSignal that passes
+// through the values of all of the signals and completes when all of the
+// signals complete.
+//
+// -subscribeCompleted: will execute the block when the signal completes.
+[[RACSignal
+ merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]]
+ subscribeCompleted:^{
+ NSLog(@"They're both done!");
+ }];
+```
+
+Signals can be chained to sequentially execute asynchronous operations, instead
+of nesting callbacks with blocks. This is similar to how [futures and promises][]
+are usually used:
+
+```objc
+// Log in the user, then load any cached messages, then fetch the remaining
+// messages from the server. After that's all done, log a message to the
+// console.
+//
+// The hypothetical -logInUser methods returns a signal that completes after
+// logging in.
+//
+// -flattenMap: will execute its block whenever the signal sends a value, and
+// return a new RACSignal that merges all of the signals returned from the block
+// into a single signal.
+[[[[client
+ logInUser]
+ flattenMap:^(User *user) {
+ // Return a signal that loads cached messages for the user.
+ return [client loadCachedMessagesForUser:user];
+ }]
+ flattenMap:^(NSArray *messages) {
+ // Return a signal that fetches any remaining messages.
+ return [client fetchMessagesAfterMessage:messages.lastObject];
+ }]
+ subscribeNext:^(NSArray *newMessages) {
+ NSLog(@"New messages: %@", newMessages);
+ } completed:^{
+ NSLog(@"Fetched all messages.");
+ }];
+```
+
+RAC even makes it easy to bind to the result of an asynchronous operation:
+
+```objc
+// Create a one-way binding so that self.imageView.image will be set the user's
+// avatar as soon as it's downloaded.
+//
+// The hypothetical -fetchUserWithUsername: method returns a signal which sends
+// the user.
+//
+// -deliverOn: creates new signals that will do their work on other queues. In
+// this example, it's used to move work to a background queue and then back to the main thread.
+//
+// -map: calls its block with each user that's fetched and returns a new
+// RACSignal that sends values returned from the block.
+RAC(self.imageView, image) = [[[[client
+ fetchUserWithUsername:@"joshaber"]
+ deliverOn:[RACScheduler scheduler]]
+ map:^(User *user) {
+ // Download the avatar (this is done on a background queue).
+ return [[NSImage alloc] initWithContentsOfURL:user.avatarURL];
+ }]
+ // Now the assignment will be done on the main thread.
+ deliverOn:RACScheduler.mainThreadScheduler];
+```
+
+That demonstrates some of what RAC can do, but it doesn't demonstrate why RAC is
+so powerful. It's hard to appreciate RAC from README-sized examples, but it
+makes it possible to write code with less state, less boilerplate, better code
+locality, and better expression of intent.
+
+For more sample code, check out [C-41][] or [GroceryList][], which are real iOS
+apps written using ReactiveCocoa. Additional information about RAC can be found
+in the [Documentation][] folder.
+
+## When to use ReactiveCocoa
+
+Upon first glance, ReactiveCocoa is very abstract, and it can be difficult to
+understand how to apply it to concrete problems.
+
+Here are some of the use cases that RAC excels at.
+
+### Handling asynchronous or event-driven data sources
+
+Much of Cocoa programming is focused on reacting to user events or changes in
+application state. Code that deals with such events can quickly become very
+complex and spaghetti-like, with lots of callbacks and state variables to handle
+ordering issues.
+
+Patterns that seem superficially different, like UI callbacks, network
+responses, and KVO notifications, actually have a lot in common. [RACSignal][]
+unifies all these different APIs so that they can be composed together and
+manipulated in the same way.
+
+For example, the following code:
+
+```objc
+
+static void *ObservationContext = &ObservationContext;
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ [LoginManager.sharedManager addObserver:self forKeyPath:@"loggingIn" options:NSKeyValueObservingOptionInitial context:&ObservationContext];
+ [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(loggedOut:) name:UserDidLogOutNotification object:LoginManager.sharedManager];
+
+ [self.usernameTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged];
+ [self.passwordTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged];
+ [self.logInButton addTarget:self action:@selector(logInPressed:) forControlEvents:UIControlEventTouchUpInside];
+}
+
+- (void)dealloc {
+ [LoginManager.sharedManager removeObserver:self forKeyPath:@"loggingIn" context:ObservationContext];
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+- (void)updateLogInButton {
+ BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0;
+ BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn;
+ self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn;
+}
+
+- (IBAction)logInPressed:(UIButton *)sender {
+ [[LoginManager sharedManager]
+ logInWithUsername:self.usernameTextField.text
+ password:self.passwordTextField.text
+ success:^{
+ self.loggedIn = YES;
+ } failure:^(NSError *error) {
+ [self presentError:error];
+ }];
+}
+
+- (void)loggedOut:(NSNotification *)notification {
+ self.loggedIn = NO;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (context == ObservationContext) {
+ [self updateLogInButton];
+ } else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+```
+
+… could be expressed in RAC like so:
+
+```objc
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ @weakify(self);
+
+ RAC(self.logInButton, enabled) = [RACSignal
+ combineLatest:@[
+ self.usernameTextField.rac_textSignal,
+ self.passwordTextField.rac_textSignal,
+ RACObserve(LoginManager.sharedManager, loggingIn),
+ RACObserve(self, loggedIn)
+ ] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {
+ return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);
+ }];
+
+ [[self.logInButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {
+ @strongify(self);
+
+ RACSignal *loginSignal = [LoginManager.sharedManager
+ logInWithUsername:self.usernameTextField.text
+ password:self.passwordTextField.text];
+
+ [loginSignal subscribeError:^(NSError *error) {
+ @strongify(self);
+ [self presentError:error];
+ } completed:^{
+ @strongify(self);
+ self.loggedIn = YES;
+ }];
+ }];
+
+ RAC(self, loggedIn) = [[NSNotificationCenter.defaultCenter
+ rac_addObserverForName:UserDidLogOutNotification object:nil]
+ mapReplace:@NO];
+}
+```
+
+### Chaining dependent operations
+
+Dependencies are most often found in network requests, where a previous request
+to the server needs to complete before the next one can be constructed, and so
+on:
+
+```objc
+[client logInWithSuccess:^{
+ [client loadCachedMessagesWithSuccess:^(NSArray *messages) {
+ [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) {
+ NSLog(@"Fetched all messages.");
+ } failure:^(NSError *error) {
+ [self presentError:error];
+ }];
+ } failure:^(NSError *error) {
+ [self presentError:error];
+ }];
+} failure:^(NSError *error) {
+ [self presentError:error];
+}];
+```
+
+ReactiveCocoa makes this pattern particularly easy:
+
+```objc
+[[[[client logIn]
+ then:^{
+ return [client loadCachedMessages];
+ }]
+ flattenMap:^(NSArray *messages) {
+ return [client fetchMessagesAfterMessage:messages.lastObject];
+ }]
+ subscribeError:^(NSError *error) {
+ [self presentError:error];
+ } completed:^{
+ NSLog(@"Fetched all messages.");
+ }];
+```
+
+### Parallelizing independent work
+
+Working with independent data sets in parallel and then combining them into
+a final result is non-trivial in Cocoa, and often involves a lot of
+synchronization:
+
+```objc
+__block NSArray *databaseObjects;
+__block NSArray *fileContents;
+
+NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
+NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{
+ databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate];
+}];
+
+NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{
+ NSMutableArray *filesInProgress = [NSMutableArray array];
+ for (NSString *path in files) {
+ [filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
+ }
+
+ fileContents = [filesInProgress copy];
+}];
+
+NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{
+ [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
+ NSLog(@"Done processing");
+}];
+
+[finishOperation addDependency:databaseOperation];
+[finishOperation addDependency:filesOperation];
+[backgroundQueue addOperation:databaseOperation];
+[backgroundQueue addOperation:filesOperation];
+[backgroundQueue addOperation:finishOperation];
+```
+
+The above code can be cleaned up and optimized by simply composing signals:
+
+```objc
+RACSignal *databaseSignal = [[databaseClient
+ fetchObjectsMatchingPredicate:predicate]
+ subscribeOn:[RACScheduler scheduler]];
+
+RACSignal *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
+ NSMutableArray *filesInProgress = [NSMutableArray array];
+ for (NSString *path in files) {
+ [filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
+ }
+
+ [subscriber sendNext:[filesInProgress copy]];
+ [subscriber sendCompleted];
+}];
+
+[[RACSignal
+ combineLatest:@[ databaseSignal, fileSignal ]
+ reduce:^ id (NSArray *databaseObjects, NSArray *fileContents) {
+ [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
+ return nil;
+ }]
+ subscribeCompleted:^{
+ NSLog(@"Done processing");
+ }];
+```
+
+### Simplifying collection transformations
+
+Higher-order functions like `map`, `filter`, `fold`/`reduce` are sorely missing
+from Foundation, leading to loop-focused code like this:
+
+```objc
+NSMutableArray *results = [NSMutableArray array];
+for (NSString *str in strings) {
+ if (str.length < 2) {
+ continue;
+ }
+
+ NSString *newString = [str stringByAppendingString:@"foobar"];
+ [results addObject:newString];
+}
+```
+
+[RACSequence][] allows any Cocoa collection to be manipulated in a uniform and
+declarative way:
+
+```objc
+RACSequence *results = [[strings.rac_sequence
+ filter:^ BOOL (NSString *str) {
+ return str.length >= 2;
+ }]
+ map:^(NSString *str) {
+ return [str stringByAppendingString:@"foobar"];
+ }];
+```
+
+## System Requirements
+
+ReactiveCocoa supports OS X 10.7+ and iOS 5.0+.
+
+## Importing ReactiveCocoa
+
+To add RAC to your application:
+
+ 1. Add the ReactiveCocoa repository as a submodule of your application's
+ repository.
+ 1. Run `script/bootstrap` from within the ReactiveCocoa folder.
+ 1. Drag and drop `ReactiveCocoaFramework/ReactiveCocoa.xcodeproj` into your
+ application's Xcode project or workspace.
+ 1. On the "Build Phases" tab of your application target, add RAC to the "Link
+ Binary With Libraries" phase.
+ * **On iOS**, add `libReactiveCocoa-iOS.a`.
+ * **On OS X**, add `ReactiveCocoa.framework`. RAC must also be added to any
+ "Copy Frameworks" build phase. If you don't already have one, simply add
+ a "Copy Files" build phase and target the "Frameworks" destination.
+ 1. Add `"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include"
+ $(inherited)` to the "Header Search Paths" build setting (this is only
+ necessary for archive builds, but it has no negative effect otherwise).
+ 1. **For iOS targets**, add `-ObjC` to the "Other Linker Flags" build setting.
+ 1. **If you added RAC to a project (not a workspace)**, you will also need to
+ add the appropriate RAC target to the "Target Dependencies" of your
+ application.
+
+If you would prefer to use [CocoaPods](http://cocoapods.org), there are some
+[ReactiveCocoa
+podspecs](https://github.com/CocoaPods/Specs/tree/master/ReactiveCocoa) that
+have been generously contributed by third parties.
+
+To see a project already set up with RAC, check out [C-41][] or [GroceryList][],
+which are real iOS apps written using ReactiveCocoa.
+
+## Standalone Development
+
+If you’re working on RAC in isolation instead of integrating it into another project, you’ll want to open `ReactiveCocoaFramework/ReactiveCocoa.xcworkspace` and not the `.xcodeproj`.
+
+## More Info
+
+ReactiveCocoa is based on .NET's [Reactive
+Extensions](http://msdn.microsoft.com/en-us/data/gg577609) (Rx). Most of the
+principles of Rx apply to RAC as well. There are some really good Rx resources
+out there:
+
+* [Reactive Extensions MSDN entry](http://msdn.microsoft.com/en-us/library/hh242985.aspx)
+* [Reactive Extensions for .NET Introduction](http://leecampbell.blogspot.com/2010/08/reactive-extensions-for-net.html)
+* [Rx - Channel 9 videos](http://channel9.msdn.com/tags/Rx/)
+* [Reactive Extensions wiki](http://rxwiki.wikidot.com/)
+* [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)
+* [Escape from Callback Hell](http://elm-lang.org/learn/Escape-from-Callback-Hell.elm)
+* [Principles of Reactive Programming on Coursera](https://www.coursera.org/course/reactive)
+
+[Basic Operators]: Documentation/BasicOperators.md
+[Documentation]: Documentation
+[Framework Overview]: Documentation/FrameworkOverview.md
+[Functional Reactive Programming]: http://en.wikipedia.org/wiki/Functional_reactive_programming
+[GroceryList]: https://github.com/jspahrsummers/GroceryList
+[Memory Management]: Documentation/MemoryManagement.md
+[NSObject+RACLifting]: ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.h
+[RACDisposable]: ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h
+[RACEvent]: ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h
+[RACMulticastConnection]: ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h
+[RACScheduler]: ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h
+[RACSequence]: ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
+[RACSignal+Operations]: ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
+[RACSignal]: ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
+[RACStream]: ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
+[RACSubscriber]: ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h
+[RAC]: ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h
+[futures and promises]: http://en.wikipedia.org/wiki/Futures_and_promises
+[C-41]: https://github.com/AshFurrow/C-41
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..58014f0
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj
@@ -0,0 +1,3128 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1646747B17FFA0610036E30B /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1668027817FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 1668028017FE75F900C724B4 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 1668027917FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.m */; };
+ 1668028317FE775200C724B4 /* UICollectionReusableViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 1668028117FE774800C724B4 /* UICollectionReusableViewRACSupportSpec.m */; };
+ 1860F414177C91B500C7B3C9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F413177C91B500C7B3C9 /* UIKit.framework */; };
+ 1860F416177C91B500C7B3C9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F415177C91B500C7B3C9 /* Foundation.framework */; };
+ 1860F418177C91B500C7B3C9 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F417177C91B500C7B3C9 /* CoreGraphics.framework */; };
+ 1860F41E177C91B500C7B3C9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1860F41C177C91B500C7B3C9 /* InfoPlist.strings */; };
+ 1860F420177C91B500C7B3C9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1860F41F177C91B500C7B3C9 /* main.m */; };
+ 1860F424177C91B500C7B3C9 /* RACAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1860F423177C91B500C7B3C9 /* RACAppDelegate.m */; };
+ 1860F426177C91B500C7B3C9 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 1860F425177C91B500C7B3C9 /* Default.png */; };
+ 1860F428177C91B500C7B3C9 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1860F427177C91B500C7B3C9 /* Default@2x.png */; };
+ 1860F42A177C91B500C7B3C9 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1860F429177C91B500C7B3C9 /* Default-568h@2x.png */; };
+ 1860F432177C91B500C7B3C9 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F431177C91B500C7B3C9 /* SenTestingKit.framework */; };
+ 1860F433177C91B500C7B3C9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F413177C91B500C7B3C9 /* UIKit.framework */; };
+ 1860F434177C91B500C7B3C9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1860F415177C91B500C7B3C9 /* Foundation.framework */; };
+ 1860F43C177C91B500C7B3C9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1860F43A177C91B500C7B3C9 /* InfoPlist.strings */; };
+ 1860F44F177C958300C7B3C9 /* UITextFieldRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C55CD817758A73008CDDCA /* UITextFieldRACSupportSpec.m */; };
+ 1860F450177C958900C7B3C9 /* UITextViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C55CDE17758C2A008CDDCA /* UITextViewRACSupportSpec.m */; };
+ 1E893381171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E893380171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m */; };
+ 1EC06B17173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC06B15173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 1EC06B18173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC06B16173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.m */; };
+ 27A887D11703DC6800040001 /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A887C81703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.m */; };
+ 27A887D21703DDEB00040001 /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 27A887C71703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 4925E806181BCC71000B2FEE /* NSControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4925E805181BCC71000B2FEE /* NSControllerRACSupportSpec.m */; };
+ 554D9E5D181064E200F21262 /* UIRefreshControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 554D9E5B181064E200F21262 /* UIRefreshControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 554D9E5E181064E200F21262 /* UIRefreshControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 554D9E5C181064E200F21262 /* UIRefreshControl+RACCommandSupport.m */; };
+ 5564537F18107203002BD2E4 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D075A72917BCB7E100C24FB7 /* RACControlCommandExamples.m */; };
+ 5564542418107275002BD2E4 /* UIRefreshControlRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5564542318107275002BD2E4 /* UIRefreshControlRACSupportSpec.m */; };
+ 557A4B5A177648C7008EF796 /* UIActionSheet+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 557A4B58177648C7008EF796 /* UIActionSheet+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 557A4B5B177648C7008EF796 /* UIActionSheet+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 557A4B59177648C7008EF796 /* UIActionSheet+RACSignalSupport.m */; };
+ 55C39DE417F1EC6E006DC60C /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8816090C1500636B49 /* NSData+RACSupport.m */; };
+ 55C39DE517F1EC6E006DC60C /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8A16090C1500636B49 /* NSFileHandle+RACSupport.m */; };
+ 55C39DE617F1EC6E006DC60C /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8C16090C1500636B49 /* NSNotificationCenter+RACSupport.m */; };
+ 55C39DE717F1EC6E006DC60C /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8E16090C1500636B49 /* NSString+RACSupport.m */; };
+ 55C39DE817F1EC6E006DC60C /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8816090C1500636B49 /* NSData+RACSupport.m */; };
+ 55C39DE917F1EC6E006DC60C /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8A16090C1500636B49 /* NSFileHandle+RACSupport.m */; };
+ 55C39DEA17F1EC6E006DC60C /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8C16090C1500636B49 /* NSNotificationCenter+RACSupport.m */; };
+ 55C39DEB17F1EC6E006DC60C /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8E16090C1500636B49 /* NSString+RACSupport.m */; };
+ 55C39DEC17F1EC84006DC60C /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8716090C1500636B49 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DED17F1EC84006DC60C /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8916090C1500636B49 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DEE17F1EC84006DC60C /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8B16090C1500636B49 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DEF17F1EC84006DC60C /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8D16090C1500636B49 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DF017F1EC84006DC60C /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8716090C1500636B49 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DF117F1EC84006DC60C /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8916090C1500636B49 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DF217F1EC84006DC60C /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8B16090C1500636B49 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 55C39DF317F1EC84006DC60C /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8D16090C1500636B49 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5EE9A7931760D61300EAF5A2 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EE9A7911760D61300EAF5A2 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5EE9A7941760D61300EAF5A2 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EE9A7921760D61300EAF5A2 /* UIButton+RACCommandSupport.m */; };
+ 5EE9A79B1760D88500EAF5A2 /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EE9A79A1760D88500EAF5A2 /* UIButtonRACSupportSpec.m */; };
+ 5F016DF717B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F016DF317B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.m */; };
+ 5F2447AD167E87C50062180C /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F2447AC167E87C50062180C /* RACKVOChannelSpec.m */; };
+ 5F45A885168CFA3E00B58A2B /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F45A883168CFA3E00B58A2B /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F45A886168CFA3E00B58A2B /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F45A883168CFA3E00B58A2B /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F45A887168CFA3E00B58A2B /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F45A884168CFA3E00B58A2B /* RACKVOChannel.m */; };
+ 5F45A888168CFA3E00B58A2B /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F45A884168CFA3E00B58A2B /* RACKVOChannel.m */; };
+ 5F6FE8531692568A00A8D7A6 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F6FE8511692568A00A8D7A6 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F6FE8541692568A00A8D7A6 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F6FE8511692568A00A8D7A6 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F6FE8551692568A00A8D7A6 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F6FE8521692568A00A8D7A6 /* RACChannel.m */; };
+ 5F6FE8561692568A00A8D7A6 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F6FE8521692568A00A8D7A6 /* RACChannel.m */; };
+ 5F70B2AF17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F70B2AD17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F70B2B017AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70B2AE17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.m */; };
+ 5F70B2BE17AB1857009AEDF9 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F70B2B617AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F70B2BF17AB1857009AEDF9 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70B2B717AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.m */; };
+ 5F70B2C017AB1857009AEDF9 /* UISlider+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F70B2B817AB1856009AEDF9 /* UISlider+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F70B2C117AB1857009AEDF9 /* UISlider+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70B2B917AB1856009AEDF9 /* UISlider+RACSignalSupport.m */; };
+ 5F70B2C217AB1857009AEDF9 /* UIStepper+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F70B2BA17AB1857009AEDF9 /* UIStepper+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F70B2C317AB1857009AEDF9 /* UIStepper+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70B2BB17AB1857009AEDF9 /* UIStepper+RACSignalSupport.m */; };
+ 5F70B2C417AB1857009AEDF9 /* UISwitch+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F70B2BC17AB1857009AEDF9 /* UISwitch+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F70B2C517AB1857009AEDF9 /* UISwitch+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F70B2BD17AB1857009AEDF9 /* UISwitch+RACSignalSupport.m */; };
+ 5F773DEA169B46670023069D /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F773DE8169B46670023069D /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F773DEB169B46670023069D /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F773DE8169B46670023069D /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5F773DEC169B46670023069D /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F773DE9169B46670023069D /* NSEnumerator+RACSequenceAdditions.m */; };
+ 5F773DED169B46670023069D /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F773DE9169B46670023069D /* NSEnumerator+RACSequenceAdditions.m */; };
+ 5F773DF0169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F773DEF169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m */; };
+ 5F7EFECF168FBC4B0037E500 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7EFECD168FBC4B0037E500 /* RACChannelExamples.m */; };
+ 5F7EFED0168FBC4B0037E500 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7EFECE168FBC4B0037E500 /* RACChannelSpec.m */; };
+ 5F9743F91694A2460024EB82 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F9743F61694A2460024EB82 /* RACEagerSequence.m */; };
+ 5F9743FA1694A2460024EB82 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F9743F61694A2460024EB82 /* RACEagerSequence.m */; };
+ 5FAF5224174D4C2000CAC810 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7DD15000FCF00163A9F /* SenTestingKit.framework */; };
+ 5FAF523E174D4D3200CAC810 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F773DEF169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m */; };
+ 5FAF523F174D4D3600CAC810 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0700F4B1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m */; };
+ 5FAF5240174D4D5600CAC810 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E58405E16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m */; };
+ 5FAF5241174D4D5600CAC810 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8801E7501644BDE200A155FE /* NSObjectRACLiftingSpec.m */; };
+ 5FAF5242174D4D5600CAC810 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E893380171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m */; };
+ 5FAF5243174D4D5600CAC810 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8851A38A16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m */; };
+ 5FAF5244174D4D5600CAC810 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDC350E1736F81800792E52 /* NSStringRACKeyPathUtilitiesSpec.m */; };
+ 5FAF5246174D4D5600CAC810 /* RACBacktraceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0870C6E16884A0600D0E11D /* RACBacktraceSpec.m */; };
+ 5FAF5247174D4D5600CAC810 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 886CEACC163DE669007632D1 /* RACBlockTrampolineSpec.m */; };
+ 5FAF5248174D4D5600CAC810 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 882CCA1D15F1564D00937D6E /* RACCommandSpec.m */; };
+ 5FAF5249174D4D5600CAC810 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E86B91669350B00667F7B /* RACCompoundDisposableSpec.m */; };
+ 5FAF524A174D4D5600CAC810 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D077A171169B79A900057BB1 /* RACEventSpec.m */; };
+ 5FAF524B174D4D5600CAC810 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D041376815D2281C004BBF80 /* RACKVOWrapperSpec.m */; };
+ 5FAF524C174D4D5600CAC810 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C5A02816924BFC0045EF05 /* RACMulticastConnectionSpec.m */; };
+ 5FAF524D174D4D5600CAC810 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F2447AC167E87C50062180C /* RACKVOChannelSpec.m */; };
+ 5FAF524E174D4D5600CAC810 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EDE76616968AB10072A780 /* RACPropertySignalExamples.m */; };
+ 5FAF524F174D4D5600CAC810 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7EFECD168FBC4B0037E500 /* RACChannelExamples.m */; };
+ 5FAF5250174D4D5600CAC810 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7EFECE168FBC4B0037E500 /* RACChannelSpec.m */; };
+ 5FAF5251174D4D5600CAC810 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8803C010166732BA00C36839 /* RACSchedulerSpec.m */; };
+ 5FAF5252174D4D5600CAC810 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70F8F164337A2007027B4 /* RACSequenceAdditionsSpec.m */; };
+ 5FAF5253174D4D5600CAC810 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70F92164337E3007027B4 /* RACSequenceExamples.m */; };
+ 5FAF5254174D4D5600CAC810 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D487051642651400DD7605 /* RACSequenceSpec.m */; };
+ 5FAF5255174D4D5600CAC810 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8820937B1501C8A600796685 /* RACSignalSpec.m */; };
+ 5FAF5256174D4D5600CAC810 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0487AB2164314430085D890 /* RACStreamExamples.m */; };
+ 5FAF5257174D4D5600CAC810 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 889D0A7F15974B2A00F833E3 /* RACSubjectSpec.m */; };
+ 5FAF5258174D4D5600CAC810 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70EC516659333005AAD03 /* RACSubscriberExamples.m */; };
+ 5FAF5259174D4D5600CAC810 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70EC7166595AD005AAD03 /* RACSubscriberSpec.m */; };
+ 5FAF525A174D4D5600CAC810 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88FC735A16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m */; };
+ 5FAF525B174D4D5600CAC810 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442A331608A9AD00636B49 /* RACTestObject.m */; };
+ 5FAF525C174D4D5600CAC810 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D02221611678910900DBD031 /* RACTupleSpec.m */; };
+ 5FAF525D174D4D5600CAC810 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A6516F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m */; };
+ 5FAF525E174D4D5600CAC810 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A6816F7BCC7004A3361 /* RACSubclassObject.m */; };
+ 5FAF5262174D4D8F00CAC810 /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FAF5261174D4D8E00CAC810 /* UIBarButtonItemRACSupportSpec.m */; };
+ 5FAF5265174D500D00CAC810 /* libReactiveCocoa-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 88F440AB153DAC820097B4C3 /* libReactiveCocoa-iOS.a */; };
+ 5FAF5289174E9CD300CAC810 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FAF5288174E9CD200CAC810 /* CoreGraphics.framework */; };
+ 5FD7DC7A174F9EAF008710B4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7C415000FCE00163A9F /* Foundation.framework */; };
+ 5FD7DC7C174F9EEB008710B4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FD7DC7B174F9EEB008710B4 /* UIKit.framework */; };
+ 5FD7DC7F174F9FC8008710B4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FD7DC7B174F9EEB008710B4 /* UIKit.framework */; };
+ 5FDC35051736F54700792E52 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDC35021736F54700792E52 /* NSString+RACKeyPathUtilities.m */; };
+ 5FDC35061736F54700792E52 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDC35021736F54700792E52 /* NSString+RACKeyPathUtilities.m */; };
+ 5FDC350F1736F81900792E52 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDC350E1736F81800792E52 /* NSStringRACKeyPathUtilitiesSpec.m */; };
+ 6E58405516F22D7500F588A6 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E58405316F22D7500F588A6 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 6E58405616F22D7500F588A6 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E58405416F22D7500F588A6 /* NSObject+RACDeallocating.m */; };
+ 6E58405D16F22F7800F588A6 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E58405416F22D7500F588A6 /* NSObject+RACDeallocating.m */; };
+ 6E58405F16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E58405E16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m */; };
+ 6EA0C08216F4AEC1006EBEB2 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E58405316F22D7500F588A6 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 7479F6E8186177D200575CDB /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 7479F6E4186177D200575CDB /* RACIndexSetSequence.m */; };
+ 7479F6E9186177D200575CDB /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 7479F6E4186177D200575CDB /* RACIndexSetSequence.m */; };
+ 7479F6EA186177D200575CDB /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 7479F6E4186177D200575CDB /* RACIndexSetSequence.m */; };
+ 74F17318186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 74F17316186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 74F17319186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 74F17316186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 74F1731A186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 74F17316186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 74F1731B186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F17317186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m */; };
+ 74F1731C186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F17317186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m */; };
+ 74F1731D186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F17317186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m */; };
+ 8801E7511644BDE200A155FE /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8801E7501644BDE200A155FE /* NSObjectRACLiftingSpec.m */; };
+ 88037F8415056328001A5B19 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7BF15000FCE00163A9F /* Cocoa.framework */; };
+ 88037FB81505645C001A5B19 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 88037F8C15056328001A5B19 /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FBB1505646C001A5B19 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF82C15008C0500163A9F /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FBC1505646C001A5B19 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF80415001CA800163A9F /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FBE1505646C001A5B19 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF7FA150019CA00163A9F /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FC11505646C001A5B19 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 882093E91501E6EE00796685 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FC21505646C001A5B19 /* NSControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 882093E61501E6CB00796685 /* NSControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88037FC71505647E001A5B19 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF82915008BB900163A9F /* NSObject+RACKVOWrapper.m */; };
+ 88037FC91505648C001A5B19 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF7FB150019CA00163A9F /* RACSubscriber.m */; };
+ 88037FCC1505648C001A5B19 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 882093EA1501E6EE00796685 /* RACCommand.m */; };
+ 88037FCD1505648C001A5B19 /* NSControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 882093E71501E6CB00796685 /* NSControl+RACCommandSupport.m */; };
+ 88037FD9150564D9001A5B19 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88037F8315056328001A5B19 /* ReactiveCocoa.framework */; };
+ 8803C011166732BA00C36839 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8803C010166732BA00C36839 /* RACSchedulerSpec.m */; };
+ 880B9176150B09190008488E /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 880B9174150B09190008488E /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 880B9177150B09190008488E /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 880B9175150B09190008488E /* RACSubject.m */; };
+ 880D7A5A16F7B351004A3361 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 880D7A5816F7B351004A3361 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 880D7A5B16F7B351004A3361 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 880D7A5816F7B351004A3361 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 880D7A5C16F7B351004A3361 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A5916F7B351004A3361 /* NSObject+RACSelectorSignal.m */; };
+ 880D7A5D16F7B351004A3361 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A5916F7B351004A3361 /* NSObject+RACSelectorSignal.m */; };
+ 880D7A6616F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A6516F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m */; };
+ 880D7A6916F7BCC7004A3361 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A6816F7BCC7004A3361 /* RACSubclassObject.m */; };
+ 881B37CC152260BF0079220B /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 881B37CA152260BF0079220B /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 881B37CD152260BF0079220B /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = 881B37CB152260BF0079220B /* RACUnit.m */; };
+ 881E86A21669304800667F7B /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E86A01669304700667F7B /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 881E86A41669304800667F7B /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E86A11669304700667F7B /* RACCompoundDisposable.m */; };
+ 881E86A51669304800667F7B /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E86A11669304700667F7B /* RACCompoundDisposable.m */; };
+ 881E86BA1669350B00667F7B /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E86B91669350B00667F7B /* RACCompoundDisposableSpec.m */; };
+ 881E87AC16695C5600667F7B /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E87AA16695C5600667F7B /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 881E87AE16695C5600667F7B /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87AB16695C5600667F7B /* RACQueueScheduler.m */; };
+ 881E87AF16695C5600667F7B /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87AB16695C5600667F7B /* RACQueueScheduler.m */; };
+ 881E87B416695EDF00667F7B /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87B116695EDF00667F7B /* RACImmediateScheduler.m */; };
+ 881E87B516695EDF00667F7B /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87B116695EDF00667F7B /* RACImmediateScheduler.m */; };
+ 881E87C41669636000667F7B /* RACSubscriptionScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E87C21669635F00667F7B /* RACSubscriptionScheduler.h */; };
+ 881E87C61669636000667F7B /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87C31669636000667F7B /* RACSubscriptionScheduler.m */; };
+ 881E87C71669636000667F7B /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87C31669636000667F7B /* RACSubscriptionScheduler.m */; };
+ 8820937C1501C8A600796685 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8820937B1501C8A600796685 /* RACSignalSpec.m */; };
+ 882CCA1E15F1564D00937D6E /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 882CCA1D15F1564D00937D6E /* RACCommandSpec.m */; };
+ 882D071917614FA7009EDA69 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D071717614FA7009EDA69 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 882D071A17614FA7009EDA69 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 882D071817614FA7009EDA69 /* RACTargetQueueScheduler.m */; };
+ 882D071F17615139009EDA69 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 882D071817614FA7009EDA69 /* RACTargetQueueScheduler.m */; };
+ 882D072117615381009EDA69 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D07201761521B009EDA69 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88302BFD1762A9E6003633BD /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 88302BFC1762A9E6003633BD /* RACTestExampleScheduler.m */; };
+ 88302BFE1762A9E6003633BD /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 88302BFC1762A9E6003633BD /* RACTestExampleScheduler.m */; };
+ 88302C2E1762C180003633BD /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88302C2D1762C180003633BD /* RACTargetQueueSchedulerSpec.m */; };
+ 88302C2F1762C180003633BD /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88302C2D1762C180003633BD /* RACTargetQueueSchedulerSpec.m */; };
+ 88302C961762EC79003633BD /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E87AA16695C5600667F7B /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88302C9B1762EC7E003633BD /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D07201761521B009EDA69 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88302CA21762F62D003633BD /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D071717614FA7009EDA69 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8837EA1816A5A33300FC3CDF /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 8837EA1516A5A33300FC3CDF /* RACKVOTrampoline.m */; };
+ 8837EA1916A5A33300FC3CDF /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 8837EA1516A5A33300FC3CDF /* RACKVOTrampoline.m */; };
+ 883A84DA1513964B006DB4C7 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84D81513964B006DB4C7 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 883A84DB1513964B006DB4C7 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84D91513964B006DB4C7 /* RACBehaviorSubject.m */; };
+ 883A84DF1513B5EC006DB4C7 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84DD1513B5EC006DB4C7 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 883A84E01513B5EC006DB4C7 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84DE1513B5EC006DB4C7 /* RACDisposable.m */; };
+ 88442A341608A9AD00636B49 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442A331608A9AD00636B49 /* RACTestObject.m */; };
+ 884476E4152367D100958F44 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 884476E2152367D100958F44 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 884476E5152367D100958F44 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 884476E3152367D100958F44 /* RACScopedDisposable.m */; };
+ 884848B615F658B800B11BD0 /* NSControlRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 884848B515F658B800B11BD0 /* NSControlRACSupportSpec.m */; };
+ 8851A38B16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8851A38A16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m */; };
+ 886678711518DCD800DE77EC /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */; };
+ 886CEACD163DE669007632D1 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 886CEACC163DE669007632D1 /* RACBlockTrampolineSpec.m */; };
+ 886CEAE2163DE942007632D1 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = 886CEAE0163DE942007632D1 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 886CEAE4163DE942007632D1 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = 886CEAE1163DE942007632D1 /* NSObject+RACLifting.m */; };
+ 886CEAE5163DE942007632D1 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = 886CEAE1163DE942007632D1 /* NSObject+RACLifting.m */; };
+ 886F702A1551CF920045D68B /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 886F70281551CF920045D68B /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 886F702B1551CF920045D68B /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 886F70291551CF920045D68B /* RACGroupedSignal.m */; };
+ 886F702C1551CF9D0045D68B /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 886F70291551CF920045D68B /* RACGroupedSignal.m */; };
+ 887ACDA9165878A8009190AD /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 887ACDA6165878A7009190AD /* NSInvocation+RACTypeParsing.m */; };
+ 887ACDAA165878A8009190AD /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 887ACDA6165878A7009190AD /* NSInvocation+RACTypeParsing.m */; };
+ 8882D4601673B0450080E7CD /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 888439A21634E10D00DED0DB /* RACBlockTrampoline.m */; };
+ 8884DD651756ACF600F6C379 /* RACSignalStartExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 8884DD641756ACF600F6C379 /* RACSignalStartExamples.m */; };
+ 8884DD6B1756B65300F6C379 /* RACSignalStartExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 8884DD641756ACF600F6C379 /* RACSignalStartExamples.m */; };
+ 88977C3E1512914A00A09EC5 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 88977C3D1512914A00A09EC5 /* RACSignal.m */; };
+ 889D0A8015974B2A00F833E3 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 889D0A7F15974B2A00F833E3 /* RACSubjectSpec.m */; };
+ 88A0B6D2165B2B09005DE8F3 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 888439A21634E10D00DED0DB /* RACBlockTrampoline.m */; };
+ 88A0B6D3165B2B77005DE8F3 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = 88FC735316114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88B76F8E153726B00053EAE2 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B76F8C153726B00053EAE2 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88B76F8F153726B00053EAE2 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B76F8D153726B00053EAE2 /* RACTuple.m */; };
+ 88C5A0241692460A0045EF05 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 88C5A0231692460A0045EF05 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88C5A026169246140045EF05 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C5A025169246140045EF05 /* RACMulticastConnection.m */; };
+ 88C5A027169246140045EF05 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C5A025169246140045EF05 /* RACMulticastConnection.m */; };
+ 88C5A02916924BFC0045EF05 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C5A02816924BFC0045EF05 /* RACMulticastConnectionSpec.m */; };
+ 88CDF7DE15000FCF00163A9F /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7DD15000FCF00163A9F /* SenTestingKit.framework */; };
+ 88CDF7DF15000FCF00163A9F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7BF15000FCE00163A9F /* Cocoa.framework */; };
+ 88CDF7E715000FCF00163A9F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 88CDF7E515000FCF00163A9F /* InfoPlist.strings */; };
+ 88D4AB3E1510F6C30011494F /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 88D4AB3C1510F6C30011494F /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88D4AB3F1510F6C30011494F /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4AB3D1510F6C30011494F /* RACReplaySubject.m */; };
+ 88DA309815071CBA00C19D0F /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DA309615071CBA00C19D0F /* RACValueTransformer.m */; };
+ 88E2C6B4153C771C00C7493C /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 88E2C6B2153C771C00C7493C /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88E2C6B5153C771C00C7493C /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E2C6B3153C771C00C7493C /* RACScheduler.m */; };
+ 88F440BA153DAD570097B4C3 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 882093EA1501E6EE00796685 /* RACCommand.m */; };
+ 88F440BC153DAD5A0097B4C3 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF7FB150019CA00163A9F /* RACSubscriber.m */; };
+ 88F440BD153DAD5C0097B4C3 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 88977C3D1512914A00A09EC5 /* RACSignal.m */; };
+ 88F440C0153DAD630097B4C3 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 880B9175150B09190008488E /* RACSubject.m */; };
+ 88F440C1153DAD640097B4C3 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4AB3D1510F6C30011494F /* RACReplaySubject.m */; };
+ 88F440C3153DAD690097B4C3 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84D91513964B006DB4C7 /* RACBehaviorSubject.m */; };
+ 88F440C5153DAD6C0097B4C3 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84DE1513B5EC006DB4C7 /* RACDisposable.m */; };
+ 88F440C6153DAD6E0097B4C3 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 884476E3152367D100958F44 /* RACScopedDisposable.m */; };
+ 88F440C9153DAD740097B4C3 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = 881B37CB152260BF0079220B /* RACUnit.m */; };
+ 88F440CA153DAD760097B4C3 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B76F8D153726B00053EAE2 /* RACTuple.m */; };
+ 88F440CB153DAD780097B4C3 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E2C6B3153C771C00C7493C /* RACScheduler.m */; };
+ 88F440CE153DAD830097B4C3 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF82915008BB900163A9F /* NSObject+RACKVOWrapper.m */; };
+ 88F440CF153DAD850097B4C3 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */; };
+ 88F440D3153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F440D1153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 88F440D4153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F440D2153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m */; };
+ 88F44263153DC2C70097B4C3 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F44260153DC0450097B4C3 /* UIControl+RACSignalSupport.m */; };
+ 88F44267153DCAC50097B4C3 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F44265153DCAC50097B4C3 /* UITextField+RACSignalSupport.m */; };
+ 88FC735716114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 88FC735416114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m */; };
+ 88FC735816114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 88FC735416114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m */; };
+ 88FC735B16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88FC735A16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m */; };
+ A1FCC27715666AA3008C9686 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = A1FCC27315666AA3008C9686 /* UITextView+RACSignalSupport.m */; };
+ A1FCC374156754A7008C9686 /* RACObjCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = A1FCC371156754A7008C9686 /* RACObjCRuntime.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ A1FCC375156754A7008C9686 /* RACObjCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = A1FCC371156754A7008C9686 /* RACObjCRuntime.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ A1FCC37B1567DED0008C9686 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A1FCC3771567DED0008C9686 /* RACDelegateProxy.m */; };
+ AC65FD52176DECB1005ED22B /* UIAlertViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = AC65FD51176DECB1005ED22B /* UIAlertViewRACSupportSpec.m */; };
+ ACB0EAF31797DDD400942FFC /* UIAlertView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = ACB0EAF11797DDD400942FFC /* UIAlertView+RACSignalSupport.m */; };
+ ACB0EAF41797DDD400942FFC /* UIAlertView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = ACB0EAF21797DDD400942FFC /* UIAlertView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BE527E9318636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = BE527E9118636F7F006349E8 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BE527E9418636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = BE527E9118636F7F006349E8 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BE527E9518636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = BE527E9118636F7F006349E8 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BE527E9618636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = BE527E9218636F7F006349E8 /* NSUserDefaults+RACSupport.m */; };
+ BE527E9718636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = BE527E9218636F7F006349E8 /* NSUserDefaults+RACSupport.m */; };
+ BE527E9818636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = BE527E9218636F7F006349E8 /* NSUserDefaults+RACSupport.m */; };
+ BE527E9F1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = BE527E9E1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m */; };
+ BE527EA01863706B006349E8 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = BE527E9E1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m */; };
+ CD11C6F318714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = CD11C6F118714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ CD11C6F418714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = CD11C6F218714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.m */; };
+ CD11C6FF18714F00007C7CFD /* RACTestTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD11C6FD18714DFB007C7CFD /* RACTestTableViewController.m */; };
+ CD11C700187151AE007C7CFD /* UITableViewHeaderFooterViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CD11C6F918714DC1007C7CFD /* UITableViewHeaderFooterViewRACSupportSpec.m */; };
+ D004BC9C177E1A2B00A5B8C5 /* UIActionSheetRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D004BC9B177E1A2B00A5B8C5 /* UIActionSheetRACSupportSpec.m */; };
+ D005A259169A3B7D00A9D2DB /* RACBacktrace.h in Headers */ = {isa = PBXBuildFile; fileRef = D02538A115E2D7FB005BACB8 /* RACBacktrace.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D00930791788AB7B00EE7E8B /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D00930771788AB7B00EE7E8B /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D009307A1788AB7B00EE7E8B /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D00930771788AB7B00EE7E8B /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D009307B1788AB7B00EE7E8B /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D00930781788AB7B00EE7E8B /* RACTestScheduler.m */; };
+ D009307C1788AB7B00EE7E8B /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D00930781788AB7B00EE7E8B /* RACTestScheduler.m */; };
+ D011F9D01782AFD400EE7E38 /* NSObjectRACAppKitBindingsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D011F9CF1782AFD400EE7E38 /* NSObjectRACAppKitBindingsSpec.m */; };
+ D013A3D91807B5ED0072B6CE /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3D51807B5ED0072B6CE /* RACErrorSignal.m */; };
+ D013A3DA1807B5ED0072B6CE /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3D51807B5ED0072B6CE /* RACErrorSignal.m */; };
+ D013A3DB1807B5ED0072B6CE /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3D51807B5ED0072B6CE /* RACErrorSignal.m */; };
+ D013A3E11807B7450072B6CE /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3DD1807B7450072B6CE /* RACEmptySignal.m */; };
+ D013A3E21807B7450072B6CE /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3DD1807B7450072B6CE /* RACEmptySignal.m */; };
+ D013A3E31807B7450072B6CE /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3DD1807B7450072B6CE /* RACEmptySignal.m */; };
+ D013A3E91807B7C30072B6CE /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3E51807B7C30072B6CE /* RACReturnSignal.m */; };
+ D013A3EA1807B7C30072B6CE /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3E51807B7C30072B6CE /* RACReturnSignal.m */; };
+ D013A3EB1807B7C30072B6CE /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3E51807B7C30072B6CE /* RACReturnSignal.m */; };
+ D013A3F21807B9690072B6CE /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3EE1807B9690072B6CE /* RACDynamicSignal.m */; };
+ D013A3F31807B9690072B6CE /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3EE1807B9690072B6CE /* RACDynamicSignal.m */; };
+ D013A3F41807B9690072B6CE /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D013A3EE1807B9690072B6CE /* RACDynamicSignal.m */; };
+ D020F3DB17F6A3E40092BED2 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D020F3DA17F6A3E40092BED2 /* RACCompoundDisposableProvider.d */; };
+ D020F3DC17F6A3E40092BED2 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D020F3DA17F6A3E40092BED2 /* RACCompoundDisposableProvider.d */; };
+ D02221621678910900DBD031 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D02221611678910900DBD031 /* RACTupleSpec.m */; };
+ D028DB74179E53CB00D1042F /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D028DB72179E53CB00D1042F /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D028DB75179E53CB00D1042F /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D028DB72179E53CB00D1042F /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D028DB76179E53CB00D1042F /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB73179E53CB00D1042F /* RACSerialDisposable.m */; };
+ D028DB77179E53CB00D1042F /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB73179E53CB00D1042F /* RACSerialDisposable.m */; };
+ D028DB7D179E591E00D1042F /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB7C179E591E00D1042F /* RACSerialDisposableSpec.m */; };
+ D028DB7E179E591E00D1042F /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB7C179E591E00D1042F /* RACSerialDisposableSpec.m */; };
+ D028DB87179E616700D1042F /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D028DB85179E616700D1042F /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D028DB88179E616700D1042F /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB86179E616700D1042F /* UITableViewCell+RACSignalSupport.m */; };
+ D0307EDF1731AAE100D83211 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0307EDC1731AAE100D83211 /* RACTupleSequence.m */; };
+ D0307EE01731AAE100D83211 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0307EDC1731AAE100D83211 /* RACTupleSequence.m */; };
+ D03525D417E2EBC90099CBAB /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03525D317E2EBC90099CBAB /* RACSignalProvider.d */; };
+ D03525D917E2FAAD0099CBAB /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03525D317E2EBC90099CBAB /* RACSignalProvider.d */; };
+ D041376915D2281C004BBF80 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D041376815D2281C004BBF80 /* RACKVOWrapperSpec.m */; };
+ D0487AB3164314430085D890 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0487AB2164314430085D890 /* RACStreamExamples.m */; };
+ D049804217F91F42001EE042 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05F9D3417984EC000FD7982 /* EXTRuntimeExtensions.m */; };
+ D05AD39617F2D5700080895B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05AD39517F2D5700080895B /* Cocoa.framework */; };
+ D05AD3BF17F2DA2A0080895B /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF7FB150019CA00163A9F /* RACSubscriber.m */; };
+ D05AD3C017F2DA300080895B /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF7FA150019CA00163A9F /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3C117F2DB100080895B /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0E22D176A8CD6007273ED /* RACPassthroughSubscriber.m */; };
+ D05AD3C217F2DB100080895B /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 888439A21634E10D00DED0DB /* RACBlockTrampoline.m */; };
+ D05AD3C317F2DB100080895B /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 881B37CA152260BF0079220B /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3C417F2DB100080895B /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = 881B37CB152260BF0079220B /* RACUnit.m */; };
+ D05AD3C517F2DB100080895B /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B76F8C153726B00053EAE2 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3C617F2DB100080895B /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B76F8D153726B00053EAE2 /* RACTuple.m */; };
+ D05AD3C717F2DB100080895B /* RACBacktrace.h in Headers */ = {isa = PBXBuildFile; fileRef = D02538A115E2D7FB005BACB8 /* RACBacktrace.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3C817F2DB100080895B /* RACBacktrace.m in Sources */ = {isa = PBXBuildFile; fileRef = D0DFBCCD15DD6D40009DADB3 /* RACBacktrace.m */; };
+ D05AD3C917F2DB100080895B /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = 88FC735316114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3CA17F2DB100080895B /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 88FC735416114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m */; };
+ D05AD3CB17F2DB100080895B /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E58405316F22D7500F588A6 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3CC17F2DB100080895B /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E58405416F22D7500F588A6 /* NSObject+RACDeallocating.m */; };
+ D05AD3CD17F2DB100080895B /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = 886CEAE0163DE942007632D1 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3CE17F2DB100080895B /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = 886CEAE1163DE942007632D1 /* NSObject+RACLifting.m */; };
+ D05AD3CF17F2DB100080895B /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 887ACDA6165878A7009190AD /* NSInvocation+RACTypeParsing.m */; };
+ D05AD3D017F2DB100080895B /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D486FF1642550100DD7605 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3D117F2DB100080895B /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D487001642550100DD7605 /* RACStream.m */; };
+ D05AD3D217F2DB1D0080895B /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF80415001CA800163A9F /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3D317F2DB1D0080895B /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 88977C3D1512914A00A09EC5 /* RACSignal.m */; };
+ D05AD3D417F2DB1D0080895B /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D910CC15F915BD00AD2DDA /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3D517F2DB1D0080895B /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D910CD15F915BD00AD2DDA /* RACSignal+Operations.m */; };
+ D05AD3D617F2DB1D0080895B /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D077A16B169B740200057BB1 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3D717F2DB1D0080895B /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D077A16C169B740200057BB1 /* RACEvent.m */; };
+ D05AD3D817F2DB1D0080895B /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 88C5A0231692460A0045EF05 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3D917F2DB1D0080895B /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C5A025169246140045EF05 /* RACMulticastConnection.m */; };
+ D05AD3DA17F2DB1D0080895B /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 886F70281551CF920045D68B /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3DB17F2DB1D0080895B /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 886F70291551CF920045D68B /* RACGroupedSignal.m */; };
+ D05AD3DC17F2DB1D0080895B /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 880B9174150B09190008488E /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3DD17F2DB1D0080895B /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 880B9175150B09190008488E /* RACSubject.m */; };
+ D05AD3DE17F2DB1D0080895B /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 88D4AB3C1510F6C30011494F /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3DF17F2DB1D0080895B /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4AB3D1510F6C30011494F /* RACReplaySubject.m */; };
+ D05AD3E017F2DB1D0080895B /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84D81513964B006DB4C7 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3E117F2DB1D0080895B /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84D91513964B006DB4C7 /* RACBehaviorSubject.m */; };
+ D05AD3E217F2DB230080895B /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84DD1513B5EC006DB4C7 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3E317F2DB230080895B /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 883A84DE1513B5EC006DB4C7 /* RACDisposable.m */; };
+ D05AD3E417F2DB230080895B /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 884476E2152367D100958F44 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3E517F2DB230080895B /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 884476E3152367D100958F44 /* RACScopedDisposable.m */; };
+ D05AD3E617F2DB230080895B /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E86A01669304700667F7B /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3E717F2DB230080895B /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E86A11669304700667F7B /* RACCompoundDisposable.m */; };
+ D05AD3E817F2DB230080895B /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D028DB72179E53CB00D1042F /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3E917F2DB230080895B /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D028DB73179E53CB00D1042F /* RACSerialDisposable.m */; };
+ D05AD3EA17F2DB270080895B /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 882093E91501E6EE00796685 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3EB17F2DB270080895B /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 882093EA1501E6EE00796685 /* RACCommand.m */; };
+ D05AD3EE17F2DB4F0080895B /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967671641EF9C00FCFF06 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3EF17F2DB4F0080895B /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967681641EF9C00FCFF06 /* RACSequence.m */; };
+ D05AD3F017F2DB4F0080895B /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967621641EF9C00FCFF06 /* RACArraySequence.m */; };
+ D05AD3F117F2DB4F0080895B /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967641641EF9C00FCFF06 /* RACDynamicSequence.m */; };
+ D05AD3F217F2DB4F0080895B /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F9743F61694A2460024EB82 /* RACEagerSequence.m */; };
+ D05AD3F317F2DB4F0080895B /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967661641EF9C00FCFF06 /* RACEmptySequence.m */; };
+ D05AD3F417F2DB4F0080895B /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9676A1641EF9C00FCFF06 /* RACStringSequence.m */; };
+ D05AD3F517F2DB4F0080895B /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EE284A164D906B006954A4 /* RACSignalSequence.m */; };
+ D05AD3F617F2DB4F0080895B /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0307EDC1731AAE100D83211 /* RACTupleSequence.m */; };
+ D05AD3F717F2DB4F0080895B /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D07CD7151731BA3900DE2394 /* RACUnarySequence.m */; };
+ D05AD3FA17F2DB5D0080895B /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 88E2C6B2153C771C00C7493C /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3FB17F2DB5D0080895B /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E2C6B3153C771C00C7493C /* RACScheduler.m */; };
+ D05AD3FC17F2DB5D0080895B /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E87AA16695C5600667F7B /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3FD17F2DB5D0080895B /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D07201761521B009EDA69 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD3FE17F2DB5D0080895B /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87AB16695C5600667F7B /* RACQueueScheduler.m */; };
+ D05AD3FF17F2DB5D0080895B /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 882D071717614FA7009EDA69 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40017F2DB5D0080895B /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 882D071817614FA7009EDA69 /* RACTargetQueueScheduler.m */; };
+ D05AD40217F2DB5D0080895B /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87B116695EDF00667F7B /* RACImmediateScheduler.m */; };
+ D05AD40317F2DB5D0080895B /* RACSubscriptionScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E87C21669635F00667F7B /* RACSubscriptionScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40417F2DB5D0080895B /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 881E87C31669636000667F7B /* RACSubscriptionScheduler.m */; };
+ D05AD40517F2DB5D0080895B /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D00930771788AB7B00EE7E8B /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40617F2DB5D0080895B /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D00930781788AB7B00EE7E8B /* RACTestScheduler.m */; };
+ D05AD40717F2DB6A0080895B /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967571641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40817F2DB6A0080895B /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967581641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m */; };
+ D05AD40917F2DB6A0080895B /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8716090C1500636B49 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40A17F2DB6A0080895B /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8816090C1500636B49 /* NSData+RACSupport.m */; };
+ D05AD40B17F2DB6A0080895B /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967591641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40C17F2DB6A0080895B /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675A1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m */; };
+ D05AD40D17F2DB6A0080895B /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F773DE8169B46670023069D /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD40E17F2DB6A0080895B /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F773DE9169B46670023069D /* NSEnumerator+RACSequenceAdditions.m */; };
+ D05AD40F17F2DB6A0080895B /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8916090C1500636B49 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41017F2DB6A0080895B /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8A16090C1500636B49 /* NSFileHandle+RACSupport.m */; };
+ D05AD41117F2DB6A0080895B /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8B16090C1500636B49 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41217F2DB6A0080895B /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8C16090C1500636B49 /* NSNotificationCenter+RACSupport.m */; };
+ D05AD41317F2DB6A0080895B /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D243B61741FA0E004359C6 /* NSObject+RACDescription.m */; };
+ D05AD41417F2DB6A0080895B /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 880D7A5816F7B351004A3361 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41517F2DB6A0080895B /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 880D7A5916F7B351004A3361 /* NSObject+RACSelectorSignal.m */; };
+ D05AD41617F2DB6A0080895B /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675B1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41717F2DB6A0080895B /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675C1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m */; };
+ D05AD41817F2DB6A0080895B /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675D1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41917F2DB6A0080895B /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675E1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m */; };
+ D05AD41A17F2DB6A0080895B /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675F1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41B17F2DB6A0080895B /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967601641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m */; };
+ D05AD41C17F2DB6A0080895B /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88442C8D16090C1500636B49 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41D17F2DB6A0080895B /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 88442C8E16090C1500636B49 /* NSString+RACSupport.m */; };
+ D05AD41E17F2DB6E0080895B /* NSControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 882093E61501E6CB00796685 /* NSControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD41F17F2DB6E0080895B /* NSControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 882093E71501E6CB00796685 /* NSControl+RACCommandSupport.m */; };
+ D05AD42017F2DB6E0080895B /* NSControl+RACTextSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A0B03916EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42117F2DB6E0080895B /* NSControl+RACTextSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0B03A16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m */; };
+ D05AD42217F2DB6E0080895B /* NSObject+RACAppKitBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F440D1153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42317F2DB6E0080895B /* NSObject+RACAppKitBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F440D2153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m */; };
+ D05AD42417F2DB6E0080895B /* NSText+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A0B01316EAA3D100C47593 /* NSText+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42517F2DB6E0080895B /* NSText+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0B01416EAA3D100C47593 /* NSText+RACSignalSupport.m */; };
+ D05AD42617F2DB840080895B /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F6FE8511692568A00A8D7A6 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42717F2DB840080895B /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F6FE8521692568A00A8D7A6 /* RACChannel.m */; };
+ D05AD42817F2DB840080895B /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F45A883168CFA3E00B58A2B /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42917F2DB840080895B /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F45A884168CFA3E00B58A2B /* RACKVOChannel.m */; };
+ D05AD42B17F2DB840080895B /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CDF82915008BB900163A9F /* NSObject+RACKVOWrapper.m */; };
+ D05AD42C17F2DB840080895B /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDC35021736F54700792E52 /* NSString+RACKeyPathUtilities.m */; };
+ D05AD42D17F2DB840080895B /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 8837EA1516A5A33300FC3CDF /* RACKVOTrampoline.m */; };
+ D05AD42E17F2DB840080895B /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF82C15008C0500163A9F /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D05AD42F17F2DB840080895B /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */; };
+ D05AD43017F2DB840080895B /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DA309615071CBA00C19D0F /* RACValueTransformer.m */; };
+ D05AD43117F2DB950080895B /* RACObjCRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = A1FCC371156754A7008C9686 /* RACObjCRuntime.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ D05AD43217F2DBCA0080895B /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03525D317E2EBC90099CBAB /* RACSignalProvider.d */; };
+ D05F9D3717984EC000FD7982 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05F9D3417984EC000FD7982 /* EXTRuntimeExtensions.m */; };
+ D05F9D3817984EC000FD7982 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05F9D3417984EC000FD7982 /* EXTRuntimeExtensions.m */; };
+ D066C796176D262500C242D2 /* UIControlRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D066C795176D262500C242D2 /* UIControlRACSupportSpec.m */; };
+ D066C79D176D263D00C242D2 /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D066C79C176D263D00C242D2 /* RACTestUIButton.m */; };
+ D0700F4C1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0700F4B1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m */; };
+ D070CBC517FB5E370017F121 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D020F3DA17F6A3E40092BED2 /* RACCompoundDisposableProvider.d */; };
+ D07200251788C57200987F70 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D07200241788C57200987F70 /* RACTestSchedulerSpec.m */; };
+ D07200261788C57200987F70 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D07200241788C57200987F70 /* RACTestSchedulerSpec.m */; };
+ D075A72A17BCB7E100C24FB7 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D075A72917BCB7E100C24FB7 /* RACControlCommandExamples.m */; };
+ D075A72B17BCB7E100C24FB7 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D075A72917BCB7E100C24FB7 /* RACControlCommandExamples.m */; };
+ D077A16D169B740200057BB1 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D077A16B169B740200057BB1 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D077A16E169B740200057BB1 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D077A16B169B740200057BB1 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D077A16F169B740200057BB1 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D077A16C169B740200057BB1 /* RACEvent.m */; };
+ D077A170169B740200057BB1 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D077A16C169B740200057BB1 /* RACEvent.m */; };
+ D077A172169B79A900057BB1 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D077A171169B79A900057BB1 /* RACEventSpec.m */; };
+ D07CD7181731BA3900DE2394 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D07CD7151731BA3900DE2394 /* RACUnarySequence.m */; };
+ D07CD7191731BA3900DE2394 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D07CD7151731BA3900DE2394 /* RACUnarySequence.m */; };
+ D0870C6F16884A0600D0E11D /* RACBacktraceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0870C6E16884A0600D0E11D /* RACBacktraceSpec.m */; };
+ D08FF264169A32D100743C6D /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 88037F8C15056328001A5B19 /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF265169A32DC00743C6D /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF7FA150019CA00163A9F /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF267169A330000743C6D /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 881B37CA152260BF0079220B /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF268169A330000743C6D /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B76F8C153726B00053EAE2 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF269169A330000743C6D /* RACBacktrace.h in Headers */ = {isa = PBXBuildFile; fileRef = D02538A115E2D7FB005BACB8 /* RACBacktrace.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26A169A330000743C6D /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = 88FC735316114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26B169A330000743C6D /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = 886CEAE0163DE942007632D1 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26C169A331A00743C6D /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D486FF1642550100DD7605 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26D169A331A00743C6D /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF80415001CA800163A9F /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26E169A331A00743C6D /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D910CC15F915BD00AD2DDA /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF26F169A331A00743C6D /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 88C5A0231692460A0045EF05 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF270169A331A00743C6D /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = 886F70281551CF920045D68B /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF271169A331A00743C6D /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 880B9174150B09190008488E /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF272169A331A00743C6D /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 88D4AB3C1510F6C30011494F /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF273169A331A00743C6D /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84D81513964B006DB4C7 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF274169A331A00743C6D /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 883A84DD1513B5EC006DB4C7 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF275169A331A00743C6D /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 884476E2152367D100958F44 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF276169A331A00743C6D /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = 881E86A01669304700667F7B /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF277169A331B00743C6D /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 882093E91501E6EE00796685 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF278169A331B00743C6D /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967671641EF9C00FCFF06 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF27F169A331B00743C6D /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 88E2C6B2153C771C00C7493C /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF280169A333400743C6D /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967571641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF281169A333400743C6D /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967591641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF282169A333400743C6D /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675B1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF283169A333400743C6D /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675D1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF284169A333400743C6D /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675F1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF285169A333400743C6D /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F4425F153DC0450097B4C3 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF286169A333400743C6D /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F44264153DCAC50097B4C3 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF287169A333400743C6D /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = A1FCC27215666AA3008C9686 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D08FF289169A333400743C6D /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CDF82C15008C0500163A9F /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D090767F17FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D090767D17FBEADE00EB087A /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D090768017FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D090767D17FBEADE00EB087A /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D090768117FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D090767D17FBEADE00EB087A /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D090768217FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D090767E17FBEADE00EB087A /* NSURLConnection+RACSupport.m */; };
+ D090768317FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D090767E17FBEADE00EB087A /* NSURLConnection+RACSupport.m */; };
+ D090768417FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D090767E17FBEADE00EB087A /* NSURLConnection+RACSupport.m */; };
+ D090768A17FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D090768917FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m */; };
+ D090768B17FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D090768917FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m */; };
+ D090768D17FBED2E00EB087A /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D090768C17FBED2E00EB087A /* test-data.json */; };
+ D090768E17FBED2E00EB087A /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D090768C17FBED2E00EB087A /* test-data.json */; };
+ D094E44917775AF200906BF7 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44517775AF200906BF7 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D094E44A17775AF200906BF7 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44517775AF200906BF7 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D094E44B17775AF200906BF7 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44617775AF200906BF7 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D094E44C17775AF200906BF7 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44617775AF200906BF7 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D094E44F17775AF200906BF7 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44817775AF200906BF7 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D094E45017775AF200906BF7 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D094E44817775AF200906BF7 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0A0B01516EAA3D100C47593 /* NSText+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A0B01316EAA3D100C47593 /* NSText+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0A0B01616EAA3D100C47593 /* NSText+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0B01416EAA3D100C47593 /* NSText+RACSignalSupport.m */; };
+ D0A0B01816EAA5CC00C47593 /* NSTextRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0B01716EAA5CC00C47593 /* NSTextRACSupportSpec.m */; };
+ D0A0B03B16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A0B03916EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0A0B03C16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0B03A16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m */; };
+ D0A0E226176A84DB007273ED /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0E225176A84DA007273ED /* RACDisposableSpec.m */; };
+ D0A0E227176A84DB007273ED /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0E225176A84DA007273ED /* RACDisposableSpec.m */; };
+ D0A0E230176A8CD6007273ED /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0E22D176A8CD6007273ED /* RACPassthroughSubscriber.m */; };
+ D0A0E231176A8CD6007273ED /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A0E22D176A8CD6007273ED /* RACPassthroughSubscriber.m */; };
+ D0C55CE217759559008CDDCA /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C55CE117759559008CDDCA /* RACDelegateProxySpec.m */; };
+ D0C70EC616659333005AAD03 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70EC516659333005AAD03 /* RACSubscriberExamples.m */; };
+ D0C70EC8166595AD005AAD03 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70EC7166595AD005AAD03 /* RACSubscriberSpec.m */; };
+ D0C70F90164337A2007027B4 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70F8F164337A2007027B4 /* RACSequenceAdditionsSpec.m */; };
+ D0C70F93164337E3007027B4 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C70F92164337E3007027B4 /* RACSequenceExamples.m */; };
+ D0D243BD1741FA13004359C6 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D243B61741FA0E004359C6 /* NSObject+RACDescription.m */; };
+ D0D243BE1741FA13004359C6 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D243B61741FA0E004359C6 /* NSObject+RACDescription.m */; };
+ D0D487011642550100DD7605 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D486FF1642550100DD7605 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0D487031642550100DD7605 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D487001642550100DD7605 /* RACStream.m */; };
+ D0D487041642550100DD7605 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D487001642550100DD7605 /* RACStream.m */; };
+ D0D487061642651400DD7605 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D487051642651400DD7605 /* RACSequenceSpec.m */; };
+ D0D910CE15F915BD00AD2DDA /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D910CC15F915BD00AD2DDA /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0D910D015F915BD00AD2DDA /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D910CD15F915BD00AD2DDA /* RACSignal+Operations.m */; };
+ D0D910D115F915BD00AD2DDA /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D910CD15F915BD00AD2DDA /* RACSignal+Operations.m */; };
+ D0DFBCCE15DD6D40009DADB3 /* RACBacktrace.m in Sources */ = {isa = PBXBuildFile; fileRef = D0DFBCCD15DD6D40009DADB3 /* RACBacktrace.m */; };
+ D0DFBCCF15DD6D40009DADB3 /* RACBacktrace.m in Sources */ = {isa = PBXBuildFile; fileRef = D0DFBCCD15DD6D40009DADB3 /* RACBacktrace.m */; };
+ D0DFBCD015DD70CC009DADB3 /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 88037F8315056328001A5B19 /* ReactiveCocoa.framework */; };
+ D0E9676B1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967571641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E9676D1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967581641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m */; };
+ D0E9676E1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967581641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m */; };
+ D0E9676F1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967591641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E967711641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675A1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m */; };
+ D0E967721641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675A1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m */; };
+ D0E967731641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675B1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E967751641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675C1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m */; };
+ D0E967761641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675C1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m */; };
+ D0E967771641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675D1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E967791641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675E1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m */; };
+ D0E9677A1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9675E1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m */; };
+ D0E9677B1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E9675F1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E9677D1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967601641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m */; };
+ D0E9677E1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967601641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m */; };
+ D0E967811641EF9C00FCFF06 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967621641EF9C00FCFF06 /* RACArraySequence.m */; };
+ D0E967821641EF9C00FCFF06 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967621641EF9C00FCFF06 /* RACArraySequence.m */; };
+ D0E967851641EF9C00FCFF06 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967641641EF9C00FCFF06 /* RACDynamicSequence.m */; };
+ D0E967861641EF9C00FCFF06 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967641641EF9C00FCFF06 /* RACDynamicSequence.m */; };
+ D0E967891641EF9C00FCFF06 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967661641EF9C00FCFF06 /* RACEmptySequence.m */; };
+ D0E9678A1641EF9C00FCFF06 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967661641EF9C00FCFF06 /* RACEmptySequence.m */; };
+ D0E9678B1641EF9C00FCFF06 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E967671641EF9C00FCFF06 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0E9678D1641EF9C00FCFF06 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967681641EF9C00FCFF06 /* RACSequence.m */; };
+ D0E9678E1641EF9C00FCFF06 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E967681641EF9C00FCFF06 /* RACSequence.m */; };
+ D0E967911641EF9C00FCFF06 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9676A1641EF9C00FCFF06 /* RACStringSequence.m */; };
+ D0E967921641EF9C00FCFF06 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E9676A1641EF9C00FCFF06 /* RACStringSequence.m */; };
+ D0EDE76716968AB10072A780 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EDE76616968AB10072A780 /* RACPropertySignalExamples.m */; };
+ D0EE284D164D906B006954A4 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EE284A164D906B006954A4 /* RACSignalSequence.m */; };
+ D0EE284E164D906B006954A4 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EE284A164D906B006954A4 /* RACSignalSequence.m */; };
+ D0F117C8179F0A95006CE68F /* libReactiveCocoa-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 88F440AB153DAC820097B4C3 /* libReactiveCocoa-iOS.a */; };
+ D0F117CC179F0B97006CE68F /* UITableViewCellRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F117CB179F0B97006CE68F /* UITableViewCellRACSupportSpec.m */; };
+ D40D7AAB18E22B5E0065BB70 /* libExpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AAA18E22B5E0065BB70 /* libExpecta.a */; };
+ D40D7AAD18E22B7E0065BB70 /* libSpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AAC18E22B7E0065BB70 /* libSpecta.a */; };
+ D40D7AAF18E22BE30065BB70 /* libExpecta-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AAE18E22BE30065BB70 /* libExpecta-iOS.a */; };
+ D40D7AB118E22BF60065BB70 /* libSpecta-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AB018E22BF60065BB70 /* libSpecta-iOS.a */; };
+ D40D7AB218E22EC60065BB70 /* libSpecta-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AB018E22BF60065BB70 /* libSpecta-iOS.a */; };
+ D40D7AB318E22EC90065BB70 /* libExpecta-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D7AAE18E22BE30065BB70 /* libExpecta-iOS.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 1860F435177C91B500C7B3C9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 88CDF7B215000FCE00163A9F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 1860F411177C91B500C7B3C9;
+ remoteInfo = "ReactiveCocoa-iOS-UIKitTestHost";
+ };
+ 88037FDA150564E9001A5B19 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 88CDF7B215000FCE00163A9F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 88037F8215056328001A5B19;
+ remoteInfo = ReactiveCocoa;
+ };
+ D0ED9DB517501806003859A6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 88CDF7B215000FCE00163A9F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 88F440AA153DAC820097B4C3;
+ remoteInfo = "ReactiveCocoa-iOS";
+ };
+ D0F117C2179F0A91006CE68F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 88CDF7B215000FCE00163A9F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 88F440AA153DAC820097B4C3;
+ remoteInfo = "ReactiveCocoa-iOS";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8820937F1501C94E00796685 /* Copy Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ D0DFBCD015DD70CC009DADB3 /* ReactiveCocoa.framework in Copy Frameworks */,
+ );
+ name = "Copy Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1668027817FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 1668027917FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 1668028117FE774800C724B4 /* UICollectionReusableViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICollectionReusableViewRACSupportSpec.m; sourceTree = "<group>"; };
+ 1860F412177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ReactiveCocoa-iOS-UIKitTestHost.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1860F413177C91B500C7B3C9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+ 1860F415177C91B500C7B3C9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+ 1860F417177C91B500C7B3C9 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+ 1860F41B177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ReactiveCocoa-iOS-UIKitTestHost-Info.plist"; sourceTree = "<group>"; };
+ 1860F41D177C91B500C7B3C9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1860F41F177C91B500C7B3C9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 1860F421177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch"; sourceTree = "<group>"; };
+ 1860F422177C91B500C7B3C9 /* RACAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACAppDelegate.h; sourceTree = "<group>"; };
+ 1860F423177C91B500C7B3C9 /* RACAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACAppDelegate.m; sourceTree = "<group>"; };
+ 1860F425177C91B500C7B3C9 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
+ 1860F427177C91B500C7B3C9 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
+ 1860F429177C91B500C7B3C9 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
+ 1860F430177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactiveCocoa-iOS-UIKitTestHostTests.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1860F431177C91B500C7B3C9 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+ 1860F43B177C91B500C7B3C9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1860F455177C96B200C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist"; path = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist"; sourceTree = SOURCE_ROOT; };
+ 1860F457177C972E00C7B3C9 /* ReactiveCocoa-iOS-UIKitTest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "ReactiveCocoa-iOS-UIKitTest-Prefix.pch"; path = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTest-Prefix.pch"; sourceTree = SOURCE_ROOT; };
+ 1E89337F171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSObjectRACPropertySubscribingExamples.h; sourceTree = "<group>"; };
+ 1E893380171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingExamples.m; sourceTree = "<group>"; };
+ 1EC06B15173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 1EC06B16173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 27A887C71703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+RACCommandSupport.h"; sourceTree = "<group>"; };
+ 27A887C81703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+RACCommandSupport.m"; sourceTree = "<group>"; };
+ 4925E805181BCC71000B2FEE /* NSControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSControllerRACSupportSpec.m; sourceTree = "<group>"; };
+ 554D9E5B181064E200F21262 /* UIRefreshControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIRefreshControl+RACCommandSupport.h"; sourceTree = "<group>"; };
+ 554D9E5C181064E200F21262 /* UIRefreshControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIRefreshControl+RACCommandSupport.m"; sourceTree = "<group>"; };
+ 5564542318107275002BD2E4 /* UIRefreshControlRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIRefreshControlRACSupportSpec.m; sourceTree = "<group>"; };
+ 557A4B58177648C7008EF796 /* UIActionSheet+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 557A4B59177648C7008EF796 /* UIActionSheet+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5EE9A7911760D61300EAF5A2 /* UIButton+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+RACCommandSupport.h"; sourceTree = "<group>"; };
+ 5EE9A7921760D61300EAF5A2 /* UIButton+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+RACCommandSupport.m"; sourceTree = "<group>"; };
+ 5EE9A79A1760D88500EAF5A2 /* UIButtonRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIButtonRACSupportSpec.m; sourceTree = "<group>"; };
+ 5F016DF217B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupportPrivate.h"; sourceTree = "<group>"; };
+ 5F016DF317B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupportPrivate.m"; sourceTree = "<group>"; };
+ 5F2447AC167E87C50062180C /* RACKVOChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannelSpec.m; sourceTree = "<group>"; };
+ 5F45A883168CFA3E00B58A2B /* RACKVOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOChannel.h; sourceTree = "<group>"; };
+ 5F45A884168CFA3E00B58A2B /* RACKVOChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannel.m; sourceTree = "<group>"; };
+ 5F6FE8511692568A00A8D7A6 /* RACChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannel.h; sourceTree = "<group>"; };
+ 5F6FE8521692568A00A8D7A6 /* RACChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannel.m; sourceTree = "<group>"; };
+ 5F70B2AD17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDatePicker+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 5F70B2AE17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDatePicker+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5F70B2B617AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISegmentedControl+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 5F70B2B717AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISegmentedControl+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5F70B2B817AB1856009AEDF9 /* UISlider+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 5F70B2B917AB1856009AEDF9 /* UISlider+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5F70B2BA17AB1857009AEDF9 /* UIStepper+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIStepper+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 5F70B2BB17AB1857009AEDF9 /* UIStepper+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIStepper+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5F70B2BC17AB1857009AEDF9 /* UISwitch+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISwitch+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 5F70B2BD17AB1857009AEDF9 /* UISwitch+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISwitch+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 5F773DE8169B46670023069D /* NSEnumerator+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEnumerator+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ 5F773DE9169B46670023069D /* NSEnumerator+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEnumerator+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ 5F773DEF169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSEnumeratorRACSequenceAdditionsSpec.m; sourceTree = "<group>"; };
+ 5F7EFECC168FBC4B0037E500 /* RACChannelExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannelExamples.h; sourceTree = "<group>"; };
+ 5F7EFECD168FBC4B0037E500 /* RACChannelExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelExamples.m; sourceTree = "<group>"; };
+ 5F7EFECE168FBC4B0037E500 /* RACChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelSpec.m; sourceTree = "<group>"; };
+ 5F9743F51694A2460024EB82 /* RACEagerSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEagerSequence.h; sourceTree = "<group>"; };
+ 5F9743F61694A2460024EB82 /* RACEagerSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEagerSequence.m; sourceTree = "<group>"; };
+ 5FAF5223174D4C2000CAC810 /* ReactiveCocoaTests-iOS.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactiveCocoaTests-iOS.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5FAF5261174D4D8E00CAC810 /* UIBarButtonItemRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBarButtonItemRACSupportSpec.m; sourceTree = "<group>"; };
+ 5FAF5288174E9CD200CAC810 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+ 5FD7DC7B174F9EEB008710B4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 5FDC35011736F54600792E52 /* NSString+RACKeyPathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACKeyPathUtilities.h"; sourceTree = "<group>"; };
+ 5FDC35021736F54700792E52 /* NSString+RACKeyPathUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACKeyPathUtilities.m"; sourceTree = "<group>"; };
+ 5FDC350E1736F81800792E52 /* NSStringRACKeyPathUtilitiesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSStringRACKeyPathUtilitiesSpec.m; sourceTree = "<group>"; };
+ 6E58405316F22D7500F588A6 /* NSObject+RACDeallocating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDeallocating.h"; sourceTree = "<group>"; };
+ 6E58405416F22D7500F588A6 /* NSObject+RACDeallocating.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDeallocating.m"; sourceTree = "<group>"; };
+ 6E58405E16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACDeallocatingSpec.m; sourceTree = "<group>"; };
+ 7479F6E3186177D200575CDB /* RACIndexSetSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACIndexSetSequence.h; sourceTree = "<group>"; };
+ 7479F6E4186177D200575CDB /* RACIndexSetSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACIndexSetSequence.m; sourceTree = "<group>"; };
+ 74F17316186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ 74F17317186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ 8801E7501644BDE200A155FE /* NSObjectRACLiftingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACLiftingSpec.m; sourceTree = "<group>"; };
+ 88037F8315056328001A5B19 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 88037F8C15056328001A5B19 /* ReactiveCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveCocoa.h; sourceTree = "<group>"; };
+ 8803C010166732BA00C36839 /* RACSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSchedulerSpec.m; sourceTree = "<group>"; };
+ 880B9174150B09190008488E /* RACSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubject.h; sourceTree = "<group>"; };
+ 880B9175150B09190008488E /* RACSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubject.m; sourceTree = "<group>"; };
+ 880D7A5816F7B351004A3361 /* NSObject+RACSelectorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACSelectorSignal.h"; sourceTree = "<group>"; };
+ 880D7A5916F7B351004A3361 /* NSObject+RACSelectorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACSelectorSignal.m"; sourceTree = "<group>"; };
+ 880D7A6516F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACSelectorSignalSpec.m; sourceTree = "<group>"; };
+ 880D7A6716F7BCC7004A3361 /* RACSubclassObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubclassObject.h; sourceTree = "<group>"; };
+ 880D7A6816F7BCC7004A3361 /* RACSubclassObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubclassObject.m; sourceTree = "<group>"; };
+ 881B37CA152260BF0079220B /* RACUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnit.h; sourceTree = "<group>"; };
+ 881B37CB152260BF0079220B /* RACUnit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnit.m; sourceTree = "<group>"; };
+ 881E86A01669304700667F7B /* RACCompoundDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCompoundDisposable.h; sourceTree = "<group>"; };
+ 881E86A11669304700667F7B /* RACCompoundDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposable.m; sourceTree = "<group>"; };
+ 881E86B91669350B00667F7B /* RACCompoundDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposableSpec.m; sourceTree = "<group>"; };
+ 881E87AA16695C5600667F7B /* RACQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACQueueScheduler.h; sourceTree = "<group>"; };
+ 881E87AB16695C5600667F7B /* RACQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACQueueScheduler.m; sourceTree = "<group>"; };
+ 881E87B016695EDF00667F7B /* RACImmediateScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACImmediateScheduler.h; sourceTree = "<group>"; };
+ 881E87B116695EDF00667F7B /* RACImmediateScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACImmediateScheduler.m; sourceTree = "<group>"; };
+ 881E87C21669635F00667F7B /* RACSubscriptionScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptionScheduler.h; sourceTree = "<group>"; };
+ 881E87C31669636000667F7B /* RACSubscriptionScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptionScheduler.m; sourceTree = "<group>"; };
+ 8820937B1501C8A600796685 /* RACSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSpec.m; sourceTree = "<group>"; };
+ 882093E61501E6CB00796685 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = "<group>"; };
+ 882093E71501E6CB00796685 /* NSControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACCommandSupport.m"; sourceTree = "<group>"; };
+ 882093E91501E6EE00796685 /* RACCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RACCommand.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 882093EA1501E6EE00796685 /* RACCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RACCommand.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
+ 882CCA1D15F1564D00937D6E /* RACCommandSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommandSpec.m; sourceTree = "<group>"; };
+ 882D071717614FA7009EDA69 /* RACTargetQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTargetQueueScheduler.h; sourceTree = "<group>"; };
+ 882D071817614FA7009EDA69 /* RACTargetQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueScheduler.m; sourceTree = "<group>"; };
+ 882D07201761521B009EDA69 /* RACQueueScheduler+Subclass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RACQueueScheduler+Subclass.h"; sourceTree = "<group>"; };
+ 88302BFB1762A9E6003633BD /* RACTestExampleScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestExampleScheduler.h; sourceTree = "<group>"; };
+ 88302BFC1762A9E6003633BD /* RACTestExampleScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestExampleScheduler.m; sourceTree = "<group>"; };
+ 88302C2D1762C180003633BD /* RACTargetQueueSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueSchedulerSpec.m; sourceTree = "<group>"; };
+ 8837EA1416A5A33300FC3CDF /* RACKVOTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOTrampoline.h; sourceTree = "<group>"; };
+ 8837EA1516A5A33300FC3CDF /* RACKVOTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOTrampoline.m; sourceTree = "<group>"; };
+ 883A84D81513964B006DB4C7 /* RACBehaviorSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBehaviorSubject.h; sourceTree = "<group>"; };
+ 883A84D91513964B006DB4C7 /* RACBehaviorSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RACBehaviorSubject.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
+ 883A84DD1513B5EC006DB4C7 /* RACDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDisposable.h; sourceTree = "<group>"; };
+ 883A84DE1513B5EC006DB4C7 /* RACDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposable.m; sourceTree = "<group>"; };
+ 88442A321608A9AD00636B49 /* RACTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestObject.h; sourceTree = "<group>"; };
+ 88442A331608A9AD00636B49 /* RACTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestObject.m; sourceTree = "<group>"; };
+ 88442C8716090C1500636B49 /* NSData+RACSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+RACSupport.h"; sourceTree = "<group>"; };
+ 88442C8816090C1500636B49 /* NSData+RACSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSData+RACSupport.m"; sourceTree = "<group>"; };
+ 88442C8916090C1500636B49 /* NSFileHandle+RACSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+RACSupport.h"; sourceTree = "<group>"; };
+ 88442C8A16090C1500636B49 /* NSFileHandle+RACSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+RACSupport.m"; sourceTree = "<group>"; };
+ 88442C8B16090C1500636B49 /* NSNotificationCenter+RACSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+RACSupport.h"; sourceTree = "<group>"; };
+ 88442C8C16090C1500636B49 /* NSNotificationCenter+RACSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+RACSupport.m"; sourceTree = "<group>"; };
+ 88442C8D16090C1500636B49 /* NSString+RACSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSupport.h"; sourceTree = "<group>"; };
+ 88442C8E16090C1500636B49 /* NSString+RACSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSupport.m"; sourceTree = "<group>"; };
+ 884476E2152367D100958F44 /* RACScopedDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScopedDisposable.h; sourceTree = "<group>"; };
+ 884476E3152367D100958F44 /* RACScopedDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScopedDisposable.m; sourceTree = "<group>"; };
+ 884848B515F658B800B11BD0 /* NSControlRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSControlRACSupportSpec.m; sourceTree = "<group>"; };
+ 8851A38A16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingSpec.m; sourceTree = "<group>"; };
+ 8857BB81152A27A9009804CC /* NSObject+RACKVOWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACKVOWrapper.h"; sourceTree = "<group>"; };
+ 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACPropertySubscribing.m"; sourceTree = "<group>"; };
+ 886CEACC163DE669007632D1 /* RACBlockTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampolineSpec.m; sourceTree = "<group>"; };
+ 886CEAE0163DE942007632D1 /* NSObject+RACLifting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACLifting.h"; sourceTree = "<group>"; };
+ 886CEAE1163DE942007632D1 /* NSObject+RACLifting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACLifting.m"; sourceTree = "<group>"; };
+ 886D98581667C86D00F22541 /* RACScheduler+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Private.h"; sourceTree = "<group>"; };
+ 886F70281551CF920045D68B /* RACGroupedSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RACGroupedSignal.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 886F70291551CF920045D68B /* RACGroupedSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACGroupedSignal.m; sourceTree = "<group>"; };
+ 887ACDA5165878A7009190AD /* NSInvocation+RACTypeParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+RACTypeParsing.h"; sourceTree = "<group>"; };
+ 887ACDA6165878A7009190AD /* NSInvocation+RACTypeParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+RACTypeParsing.m"; sourceTree = "<group>"; };
+ 888439A11634E10D00DED0DB /* RACBlockTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBlockTrampoline.h; sourceTree = "<group>"; };
+ 888439A21634E10D00DED0DB /* RACBlockTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampoline.m; sourceTree = "<group>"; };
+ 8884DD641756ACF600F6C379 /* RACSignalStartExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalStartExamples.m; sourceTree = "<group>"; };
+ 8884DD6A1756AD3600F6C379 /* RACSignalStartExamples.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACSignalStartExamples.h; sourceTree = "<group>"; };
+ 88977C3D1512914A00A09EC5 /* RACSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignal.m; sourceTree = "<group>"; };
+ 889D0A7F15974B2A00F833E3 /* RACSubjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubjectSpec.m; sourceTree = "<group>"; };
+ 88B76F8C153726B00053EAE2 /* RACTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTuple.h; sourceTree = "<group>"; };
+ 88B76F8D153726B00053EAE2 /* RACTuple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTuple.m; sourceTree = "<group>"; };
+ 88C5A0231692460A0045EF05 /* RACMulticastConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACMulticastConnection.h; sourceTree = "<group>"; };
+ 88C5A025169246140045EF05 /* RACMulticastConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnection.m; sourceTree = "<group>"; };
+ 88C5A02816924BFC0045EF05 /* RACMulticastConnectionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnectionSpec.m; sourceTree = "<group>"; };
+ 88CDF7BF15000FCE00163A9F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 88CDF7C315000FCE00163A9F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+ 88CDF7C415000FCE00163A9F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 88CDF7C715000FCE00163A9F /* ReactiveCocoa-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "ReactiveCocoa-Info.plist"; sourceTree = "<group>"; };
+ 88CDF7C915000FCE00163A9F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 88CDF7CD15000FCE00163A9F /* ReactiveCocoa-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoa-Prefix.pch"; sourceTree = "<group>"; };
+ 88CDF7DC15000FCF00163A9F /* ReactiveCocoaTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 88CDF7DD15000FCF00163A9F /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+ 88CDF7E415000FCF00163A9F /* ReactiveCocoaTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "ReactiveCocoaTests-Info.plist"; sourceTree = "<group>"; };
+ 88CDF7E615000FCF00163A9F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 88CDF7FA150019CA00163A9F /* RACSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriber.h; sourceTree = "<group>"; };
+ 88CDF7FB150019CA00163A9F /* RACSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriber.m; sourceTree = "<group>"; };
+ 88CDF80415001CA800163A9F /* RACSignal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACSignal.h; sourceTree = "<group>"; };
+ 88CDF82915008BB900163A9F /* NSObject+RACKVOWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACKVOWrapper.m"; sourceTree = "<group>"; };
+ 88CDF82C15008C0500163A9F /* NSObject+RACPropertySubscribing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "NSObject+RACPropertySubscribing.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 88D4AB3C1510F6C30011494F /* RACReplaySubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReplaySubject.h; sourceTree = "<group>"; };
+ 88D4AB3D1510F6C30011494F /* RACReplaySubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RACReplaySubject.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
+ 88DA309515071CBA00C19D0F /* RACValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACValueTransformer.h; sourceTree = "<group>"; };
+ 88DA309615071CBA00C19D0F /* RACValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACValueTransformer.m; sourceTree = "<group>"; };
+ 88E2C6B2153C771C00C7493C /* RACScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScheduler.h; sourceTree = "<group>"; };
+ 88E2C6B3153C771C00C7493C /* RACScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScheduler.m; sourceTree = "<group>"; };
+ 88F440AB153DAC820097B4C3 /* libReactiveCocoa-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libReactiveCocoa-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 88F440D1153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACAppKitBindings.h"; sourceTree = "<group>"; };
+ 88F440D2153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACAppKitBindings.m"; sourceTree = "<group>"; };
+ 88F4425F153DC0450097B4C3 /* UIControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 88F44260153DC0450097B4C3 /* UIControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 88F44264153DCAC50097B4C3 /* UITextField+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+RACSignalSupport.h"; sourceTree = "<group>"; };
+ 88F44265153DCAC50097B4C3 /* UITextField+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+RACSignalSupport.m"; sourceTree = "<group>"; };
+ 88F5870515361C170084BD32 /* RACMulticastConnection+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "RACMulticastConnection+Private.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 88FC735316114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptingAssignmentTrampoline.h; sourceTree = "<group>"; };
+ 88FC735416114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampoline.m; sourceTree = "<group>"; };
+ 88FC735A16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampolineSpec.m; sourceTree = "<group>"; };
+ A1FCC27215666AA3008C9686 /* UITextView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+RACSignalSupport.h"; sourceTree = "<group>"; };
+ A1FCC27315666AA3008C9686 /* UITextView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+RACSignalSupport.m"; sourceTree = "<group>"; };
+ A1FCC370156754A7008C9686 /* RACObjCRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntime.h; sourceTree = "<group>"; };
+ A1FCC371156754A7008C9686 /* RACObjCRuntime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntime.m; sourceTree = "<group>"; };
+ A1FCC3761567DED0008C9686 /* RACDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDelegateProxy.h; sourceTree = "<group>"; };
+ A1FCC3771567DED0008C9686 /* RACDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxy.m; sourceTree = "<group>"; };
+ AC65FD51176DECB1005ED22B /* UIAlertViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAlertViewRACSupportSpec.m; sourceTree = "<group>"; };
+ ACB0EAF11797DDD400942FFC /* UIAlertView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+RACSignalSupport.m"; sourceTree = "<group>"; };
+ ACB0EAF21797DDD400942FFC /* UIAlertView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+RACSignalSupport.h"; sourceTree = "<group>"; };
+ BE527E9118636F7F006349E8 /* NSUserDefaults+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+RACSupport.h"; sourceTree = "<group>"; };
+ BE527E9218636F7F006349E8 /* NSUserDefaults+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+RACSupport.m"; sourceTree = "<group>"; };
+ BE527E9E1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsRACSupportSpec.m; sourceTree = "<group>"; };
+ CD11C6F118714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewHeaderFooterView+RACSignalSupport.h"; sourceTree = "<group>"; };
+ CD11C6F218714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewHeaderFooterView+RACSignalSupport.m"; sourceTree = "<group>"; };
+ CD11C6F918714DC1007C7CFD /* UITableViewHeaderFooterViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UITableViewHeaderFooterViewRACSupportSpec.m; sourceTree = "<group>"; };
+ CD11C6FC18714DFB007C7CFD /* RACTestTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestTableViewController.h; sourceTree = "<group>"; };
+ CD11C6FD18714DFB007C7CFD /* RACTestTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestTableViewController.m; sourceTree = "<group>"; };
+ D004BC9B177E1A2B00A5B8C5 /* UIActionSheetRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIActionSheetRACSupportSpec.m; path = ReactiveCocoaTests/UIActionSheetRACSupportSpec.m; sourceTree = SOURCE_ROOT; };
+ D00930771788AB7B00EE7E8B /* RACTestScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestScheduler.h; sourceTree = "<group>"; };
+ D00930781788AB7B00EE7E8B /* RACTestScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestScheduler.m; sourceTree = "<group>"; };
+ D011F9CF1782AFD400EE7E38 /* NSObjectRACAppKitBindingsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACAppKitBindingsSpec.m; sourceTree = "<group>"; };
+ D013A3D41807B5ED0072B6CE /* RACErrorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACErrorSignal.h; sourceTree = "<group>"; };
+ D013A3D51807B5ED0072B6CE /* RACErrorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACErrorSignal.m; sourceTree = "<group>"; };
+ D013A3DC1807B7450072B6CE /* RACEmptySignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySignal.h; sourceTree = "<group>"; };
+ D013A3DD1807B7450072B6CE /* RACEmptySignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySignal.m; sourceTree = "<group>"; };
+ D013A3E41807B7C30072B6CE /* RACReturnSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReturnSignal.h; sourceTree = "<group>"; };
+ D013A3E51807B7C30072B6CE /* RACReturnSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReturnSignal.m; sourceTree = "<group>"; };
+ D013A3ED1807B9690072B6CE /* RACDynamicSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSignal.h; sourceTree = "<group>"; };
+ D013A3EE1807B9690072B6CE /* RACDynamicSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSignal.m; sourceTree = "<group>"; };
+ D01DB9AE166819B9003E8F7F /* ReactiveCocoaTests-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoaTests-Prefix.pch"; sourceTree = "<group>"; };
+ D020F3DA17F6A3E40092BED2 /* RACCompoundDisposableProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACCompoundDisposableProvider.d; sourceTree = "<group>"; };
+ D02221611678910900DBD031 /* RACTupleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSpec.m; sourceTree = "<group>"; };
+ D02538A115E2D7FB005BACB8 /* RACBacktrace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACBacktrace.h; sourceTree = "<group>"; };
+ D028DB72179E53CB00D1042F /* RACSerialDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSerialDisposable.h; sourceTree = "<group>"; };
+ D028DB73179E53CB00D1042F /* RACSerialDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposable.m; sourceTree = "<group>"; };
+ D028DB7C179E591E00D1042F /* RACSerialDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposableSpec.m; sourceTree = "<group>"; };
+ D028DB85179E616700D1042F /* UITableViewCell+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+RACSignalSupport.h"; sourceTree = "<group>"; };
+ D028DB86179E616700D1042F /* UITableViewCell+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+RACSignalSupport.m"; sourceTree = "<group>"; };
+ D0307EDB1731AAE100D83211 /* RACTupleSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTupleSequence.h; sourceTree = "<group>"; };
+ D0307EDC1731AAE100D83211 /* RACTupleSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSequence.m; sourceTree = "<group>"; };
+ D03525D317E2EBC90099CBAB /* RACSignalProvider.d */ = {isa = PBXFileReference; explicitFileType = sourcecode.dtrace; fileEncoding = 4; path = RACSignalProvider.d; sourceTree = "<group>"; };
+ D041376815D2281C004BBF80 /* RACKVOWrapperSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOWrapperSpec.m; sourceTree = "<group>"; };
+ D0487AB1164314430085D890 /* RACStreamExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStreamExamples.h; sourceTree = "<group>"; };
+ D0487AB2164314430085D890 /* RACStreamExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStreamExamples.m; sourceTree = "<group>"; };
+ D05AD39417F2D56F0080895B /* libReactiveCocoa-Mac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libReactiveCocoa-Mac.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ D05AD39517F2D5700080895B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ D05AD39717F2D5700080895B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ D05AD3A317F2D5700080895B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
+ D05F9D3317984EC000FD7982 /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTRuntimeExtensions.h; sourceTree = "<group>"; };
+ D05F9D3417984EC000FD7982 /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTRuntimeExtensions.m; sourceTree = "<group>"; };
+ D066C795176D262500C242D2 /* UIControlRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIControlRACSupportSpec.m; sourceTree = "<group>"; };
+ D066C79B176D263D00C242D2 /* RACTestUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestUIButton.h; sourceTree = "<group>"; };
+ D066C79C176D263D00C242D2 /* RACTestUIButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestUIButton.m; sourceTree = "<group>"; };
+ D0700F4B1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSNotificationCenterRACSupportSpec.m; sourceTree = "<group>"; };
+ D07200241788C57200987F70 /* RACTestSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestSchedulerSpec.m; sourceTree = "<group>"; };
+ D075A72817BCB7E100C24FB7 /* RACControlCommandExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACControlCommandExamples.h; sourceTree = "<group>"; };
+ D075A72917BCB7E100C24FB7 /* RACControlCommandExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACControlCommandExamples.m; sourceTree = "<group>"; };
+ D077A16B169B740200057BB1 /* RACEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEvent.h; sourceTree = "<group>"; };
+ D077A16C169B740200057BB1 /* RACEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEvent.m; sourceTree = "<group>"; };
+ D077A171169B79A900057BB1 /* RACEventSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEventSpec.m; sourceTree = "<group>"; };
+ D07CD7141731BA3900DE2394 /* RACUnarySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnarySequence.h; sourceTree = "<group>"; };
+ D07CD7151731BA3900DE2394 /* RACUnarySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnarySequence.m; sourceTree = "<group>"; };
+ D07E9489179DD21E00A6F609 /* RACStream+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RACStream+Private.h"; sourceTree = "<group>"; };
+ D0870C6E16884A0600D0E11D /* RACBacktraceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBacktraceSpec.m; sourceTree = "<group>"; };
+ D090767D17FBEADE00EB087A /* NSURLConnection+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLConnection+RACSupport.h"; sourceTree = "<group>"; };
+ D090767E17FBEADE00EB087A /* NSURLConnection+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLConnection+RACSupport.m"; sourceTree = "<group>"; };
+ D090768917FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLConnectionRACSupportSpec.m; sourceTree = "<group>"; };
+ D090768C17FBED2E00EB087A /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = "<group>"; };
+ D094E44517775AF200906BF7 /* EXTKeyPathCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTKeyPathCoding.h; sourceTree = "<group>"; };
+ D094E44617775AF200906BF7 /* EXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTScope.h; sourceTree = "<group>"; };
+ D094E44817775AF200906BF7 /* metamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metamacros.h; sourceTree = "<group>"; };
+ D094E45317775B1000906BF7 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = "<group>"; };
+ D094E45517775B1000906BF7 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+ D094E45617775B1000906BF7 /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = "<group>"; };
+ D094E45717775B1000906BF7 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+ D094E45817775B1000906BF7 /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = "<group>"; };
+ D094E45A17775B1000906BF7 /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = "<group>"; };
+ D094E45B17775B1000906BF7 /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = "<group>"; };
+ D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = "<group>"; };
+ D094E45E17775B1000906BF7 /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = "<group>"; };
+ D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = "<group>"; };
+ D094E46117775B1000906BF7 /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = "<group>"; };
+ D094E46217775B1000906BF7 /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = "<group>"; };
+ D094E46317775B1000906BF7 /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = "<group>"; };
+ D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = "<group>"; };
+ D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = "<group>"; };
+ D094E46617775B1000906BF7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
+ D0A0B01316EAA3D100C47593 /* NSText+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSText+RACSignalSupport.h"; sourceTree = "<group>"; };
+ D0A0B01416EAA3D100C47593 /* NSText+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSText+RACSignalSupport.m"; sourceTree = "<group>"; };
+ D0A0B01716EAA5CC00C47593 /* NSTextRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTextRACSupportSpec.m; sourceTree = "<group>"; };
+ D0A0B03916EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACTextSignalSupport.h"; sourceTree = "<group>"; };
+ D0A0B03A16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACTextSignalSupport.m"; sourceTree = "<group>"; };
+ D0A0E225176A84DA007273ED /* RACDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposableSpec.m; sourceTree = "<group>"; };
+ D0A0E22C176A8CD6007273ED /* RACPassthroughSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPassthroughSubscriber.h; sourceTree = "<group>"; };
+ D0A0E22D176A8CD6007273ED /* RACPassthroughSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPassthroughSubscriber.m; sourceTree = "<group>"; };
+ D0C55CD817758A73008CDDCA /* UITextFieldRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UITextFieldRACSupportSpec.m; sourceTree = "<group>"; };
+ D0C55CDE17758C2A008CDDCA /* UITextViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UITextViewRACSupportSpec.m; sourceTree = "<group>"; };
+ D0C55CE117759559008CDDCA /* RACDelegateProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxySpec.m; sourceTree = "<group>"; };
+ D0C70EC416659333005AAD03 /* RACSubscriberExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriberExamples.h; sourceTree = "<group>"; };
+ D0C70EC516659333005AAD03 /* RACSubscriberExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberExamples.m; sourceTree = "<group>"; };
+ D0C70EC7166595AD005AAD03 /* RACSubscriberSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberSpec.m; sourceTree = "<group>"; };
+ D0C70F8F164337A2007027B4 /* RACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceAdditionsSpec.m; sourceTree = "<group>"; };
+ D0C70F91164337E3007027B4 /* RACSequenceExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequenceExamples.h; sourceTree = "<group>"; };
+ D0C70F92164337E3007027B4 /* RACSequenceExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceExamples.m; sourceTree = "<group>"; };
+ D0D243B51741FA0E004359C6 /* NSObject+RACDescription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDescription.h"; sourceTree = "<group>"; };
+ D0D243B61741FA0E004359C6 /* NSObject+RACDescription.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDescription.m"; sourceTree = "<group>"; };
+ D0D486FF1642550100DD7605 /* RACStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStream.h; sourceTree = "<group>"; };
+ D0D487001642550100DD7605 /* RACStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStream.m; sourceTree = "<group>"; };
+ D0D487051642651400DD7605 /* RACSequenceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceSpec.m; sourceTree = "<group>"; };
+ D0D910CC15F915BD00AD2DDA /* RACSignal+Operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+Operations.h"; sourceTree = "<group>"; };
+ D0D910CD15F915BD00AD2DDA /* RACSignal+Operations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+Operations.m"; sourceTree = "<group>"; };
+ D0DFBCCD15DD6D40009DADB3 /* RACBacktrace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBacktrace.m; sourceTree = "<group>"; };
+ D0E967571641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ D0E967581641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ D0E967591641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ D0E9675A1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ D0E9675B1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOrderedSet+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ D0E9675C1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOrderedSet+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ D0E9675D1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ D0E9675E1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ D0E9675F1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSequenceAdditions.h"; sourceTree = "<group>"; };
+ D0E967601641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSequenceAdditions.m"; sourceTree = "<group>"; };
+ D0E967611641EF9C00FCFF06 /* RACArraySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACArraySequence.h; sourceTree = "<group>"; };
+ D0E967621641EF9C00FCFF06 /* RACArraySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACArraySequence.m; sourceTree = "<group>"; };
+ D0E967631641EF9C00FCFF06 /* RACDynamicSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSequence.h; sourceTree = "<group>"; };
+ D0E967641641EF9C00FCFF06 /* RACDynamicSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSequence.m; sourceTree = "<group>"; };
+ D0E967651641EF9C00FCFF06 /* RACEmptySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySequence.h; sourceTree = "<group>"; };
+ D0E967661641EF9C00FCFF06 /* RACEmptySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySequence.m; sourceTree = "<group>"; };
+ D0E967671641EF9C00FCFF06 /* RACSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RACSequence.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ D0E967681641EF9C00FCFF06 /* RACSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequence.m; sourceTree = "<group>"; };
+ D0E967691641EF9C00FCFF06 /* RACStringSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStringSequence.h; sourceTree = "<group>"; };
+ D0E9676A1641EF9C00FCFF06 /* RACStringSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStringSequence.m; sourceTree = "<group>"; };
+ D0EDE76516968AB10072A780 /* RACPropertySignalExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPropertySignalExamples.h; sourceTree = "<group>"; };
+ D0EDE76616968AB10072A780 /* RACPropertySignalExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPropertySignalExamples.m; sourceTree = "<group>"; };
+ D0EE2849164D906B006954A4 /* RACSignalSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignalSequence.h; sourceTree = "<group>"; };
+ D0EE284A164D906B006954A4 /* RACSignalSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSequence.m; sourceTree = "<group>"; };
+ D0F117CB179F0B97006CE68F /* UITableViewCellRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UITableViewCellRACSupportSpec.m; sourceTree = "<group>"; };
+ D0FAEC02176AEEE600D3C1A7 /* RACSubscriber+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RACSubscriber+Private.h"; sourceTree = "<group>"; };
+ D40D7AAA18E22B5E0065BB70 /* libExpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libExpecta.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ D40D7AAC18E22B7E0065BB70 /* libSpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libSpecta.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ D40D7AAE18E22BE30065BB70 /* libExpecta-iOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libExpecta-iOS.a"; path = "../external/expecta/build/Debug-iphoneos/libExpecta-iOS.a"; sourceTree = "<group>"; };
+ D40D7AB018E22BF60065BB70 /* libSpecta-iOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libSpecta-iOS.a"; path = "../external/specta/build/Debug-iphoneos/libSpecta-iOS.a"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 1860F40F177C91B500C7B3C9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0F117C8179F0A95006CE68F /* libReactiveCocoa-iOS.a in Frameworks */,
+ 1860F414177C91B500C7B3C9 /* UIKit.framework in Frameworks */,
+ 1860F416177C91B500C7B3C9 /* Foundation.framework in Frameworks */,
+ 1860F418177C91B500C7B3C9 /* CoreGraphics.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 1860F42C177C91B500C7B3C9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1860F432177C91B500C7B3C9 /* SenTestingKit.framework in Frameworks */,
+ 1860F433177C91B500C7B3C9 /* UIKit.framework in Frameworks */,
+ 1860F434177C91B500C7B3C9 /* Foundation.framework in Frameworks */,
+ D40D7AB218E22EC60065BB70 /* libSpecta-iOS.a in Frameworks */,
+ D40D7AB318E22EC90065BB70 /* libExpecta-iOS.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 5FAF521F174D4C2000CAC810 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5FD7DC7C174F9EEB008710B4 /* UIKit.framework in Frameworks */,
+ 5FAF5289174E9CD300CAC810 /* CoreGraphics.framework in Frameworks */,
+ 5FAF5265174D500D00CAC810 /* libReactiveCocoa-iOS.a in Frameworks */,
+ 5FAF5224174D4C2000CAC810 /* SenTestingKit.framework in Frameworks */,
+ D40D7AAF18E22BE30065BB70 /* libExpecta-iOS.a in Frameworks */,
+ D40D7AB118E22BF60065BB70 /* libSpecta-iOS.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88037F7F15056328001A5B19 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 88037F8415056328001A5B19 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88CDF7D815000FCF00163A9F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 88037FD9150564D9001A5B19 /* ReactiveCocoa.framework in Frameworks */,
+ 88CDF7DE15000FCF00163A9F /* SenTestingKit.framework in Frameworks */,
+ 88CDF7DF15000FCF00163A9F /* Cocoa.framework in Frameworks */,
+ D40D7AAB18E22B5E0065BB70 /* libExpecta.a in Frameworks */,
+ D40D7AAD18E22B7E0065BB70 /* libSpecta.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88F440A8153DAC820097B4C3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5FD7DC7A174F9EAF008710B4 /* Foundation.framework in Frameworks */,
+ 5FD7DC7F174F9FC8008710B4 /* UIKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D05AD39117F2D56F0080895B /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D05AD39617F2D5700080895B /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 1860F419177C91B500C7B3C9 /* UIKitTestHost */ = {
+ isa = PBXGroup;
+ children = (
+ 1860F422177C91B500C7B3C9 /* RACAppDelegate.h */,
+ 1860F423177C91B500C7B3C9 /* RACAppDelegate.m */,
+ CD11C6FC18714DFB007C7CFD /* RACTestTableViewController.h */,
+ CD11C6FD18714DFB007C7CFD /* RACTestTableViewController.m */,
+ 1860F41A177C91B500C7B3C9 /* Supporting Files */,
+ );
+ path = UIKitTestHost;
+ sourceTree = "<group>";
+ };
+ 1860F41A177C91B500C7B3C9 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 1860F41B177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost-Info.plist */,
+ 1860F41C177C91B500C7B3C9 /* InfoPlist.strings */,
+ 1860F41F177C91B500C7B3C9 /* main.m */,
+ 1860F421177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch */,
+ 1860F425177C91B500C7B3C9 /* Default.png */,
+ 1860F427177C91B500C7B3C9 /* Default@2x.png */,
+ 1860F429177C91B500C7B3C9 /* Default-568h@2x.png */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 1860F437177C91B500C7B3C9 /* UIKitTests */ = {
+ isa = PBXGroup;
+ children = (
+ D004BC9B177E1A2B00A5B8C5 /* UIActionSheetRACSupportSpec.m */,
+ 1668028117FE774800C724B4 /* UICollectionReusableViewRACSupportSpec.m */,
+ D0F117CB179F0B97006CE68F /* UITableViewCellRACSupportSpec.m */,
+ CD11C6F918714DC1007C7CFD /* UITableViewHeaderFooterViewRACSupportSpec.m */,
+ D0C55CD817758A73008CDDCA /* UITextFieldRACSupportSpec.m */,
+ D0C55CDE17758C2A008CDDCA /* UITextViewRACSupportSpec.m */,
+ );
+ path = UIKitTests;
+ sourceTree = "<group>";
+ };
+ 1860F438177C91B500C7B3C9 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 1860F455177C96B200C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist */,
+ 1860F457177C972E00C7B3C9 /* ReactiveCocoa-iOS-UIKitTest-Prefix.pch */,
+ 1860F43A177C91B500C7B3C9 /* InfoPlist.strings */,
+ );
+ name = "Supporting Files";
+ path = UIKitTestHost;
+ sourceTree = "<group>";
+ };
+ 1860F44E177C949A00C7B3C9 /* UIKit */ = {
+ isa = PBXGroup;
+ children = (
+ 1860F437177C91B500C7B3C9 /* UIKitTests */,
+ 1860F419177C91B500C7B3C9 /* UIKitTestHost */,
+ 1860F438177C91B500C7B3C9 /* Supporting Files */,
+ );
+ path = UIKit;
+ sourceTree = "<group>";
+ };
+ 5FAF525F174D4D6100CAC810 /* OS X */ = {
+ isa = PBXGroup;
+ children = (
+ 884848B515F658B800B11BD0 /* NSControlRACSupportSpec.m */,
+ D011F9CF1782AFD400EE7E38 /* NSObjectRACAppKitBindingsSpec.m */,
+ D0A0B01716EAA5CC00C47593 /* NSTextRACSupportSpec.m */,
+ 4925E805181BCC71000B2FEE /* NSControllerRACSupportSpec.m */,
+ );
+ name = "OS X";
+ sourceTree = "<group>";
+ };
+ 5FAF5260174D4D7100CAC810 /* iOS */ = {
+ isa = PBXGroup;
+ children = (
+ 1860F44E177C949A00C7B3C9 /* UIKit */,
+ D0C55CE117759559008CDDCA /* RACDelegateProxySpec.m */,
+ D066C79B176D263D00C242D2 /* RACTestUIButton.h */,
+ D066C79C176D263D00C242D2 /* RACTestUIButton.m */,
+ AC65FD51176DECB1005ED22B /* UIAlertViewRACSupportSpec.m */,
+ 5FAF5261174D4D8E00CAC810 /* UIBarButtonItemRACSupportSpec.m */,
+ 5EE9A79A1760D88500EAF5A2 /* UIButtonRACSupportSpec.m */,
+ D066C795176D262500C242D2 /* UIControlRACSupportSpec.m */,
+ 5564542318107275002BD2E4 /* UIRefreshControlRACSupportSpec.m */,
+ );
+ name = iOS;
+ sourceTree = "<group>";
+ };
+ 88977C5915129AB200A09EC5 /* KVO + Bindings */ = {
+ isa = PBXGroup;
+ children = (
+ 5F6FE8511692568A00A8D7A6 /* RACChannel.h */,
+ 5F6FE8521692568A00A8D7A6 /* RACChannel.m */,
+ 5F45A883168CFA3E00B58A2B /* RACKVOChannel.h */,
+ 5F45A884168CFA3E00B58A2B /* RACKVOChannel.m */,
+ 8857BB81152A27A9009804CC /* NSObject+RACKVOWrapper.h */,
+ 88CDF82915008BB900163A9F /* NSObject+RACKVOWrapper.m */,
+ 5FDC35011736F54600792E52 /* NSString+RACKeyPathUtilities.h */,
+ 5FDC35021736F54700792E52 /* NSString+RACKeyPathUtilities.m */,
+ 8837EA1416A5A33300FC3CDF /* RACKVOTrampoline.h */,
+ 8837EA1516A5A33300FC3CDF /* RACKVOTrampoline.m */,
+ 88CDF82C15008C0500163A9F /* NSObject+RACPropertySubscribing.h */,
+ 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */,
+ 88DA309515071CBA00C19D0F /* RACValueTransformer.h */,
+ 88DA309615071CBA00C19D0F /* RACValueTransformer.m */,
+ );
+ name = "KVO + Bindings";
+ sourceTree = "<group>";
+ };
+ 889C04BB155DA37600F19F0C /* Foundation Support */ = {
+ isa = PBXGroup;
+ children = (
+ D0E967571641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h */,
+ D0E967581641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m */,
+ 88442C8716090C1500636B49 /* NSData+RACSupport.h */,
+ 88442C8816090C1500636B49 /* NSData+RACSupport.m */,
+ D0E967591641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h */,
+ D0E9675A1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m */,
+ 5F773DE8169B46670023069D /* NSEnumerator+RACSequenceAdditions.h */,
+ 5F773DE9169B46670023069D /* NSEnumerator+RACSequenceAdditions.m */,
+ 88442C8916090C1500636B49 /* NSFileHandle+RACSupport.h */,
+ 88442C8A16090C1500636B49 /* NSFileHandle+RACSupport.m */,
+ 74F17316186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h */,
+ 74F17317186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m */,
+ 88442C8B16090C1500636B49 /* NSNotificationCenter+RACSupport.h */,
+ 88442C8C16090C1500636B49 /* NSNotificationCenter+RACSupport.m */,
+ D0D243B51741FA0E004359C6 /* NSObject+RACDescription.h */,
+ D0D243B61741FA0E004359C6 /* NSObject+RACDescription.m */,
+ 880D7A5816F7B351004A3361 /* NSObject+RACSelectorSignal.h */,
+ 880D7A5916F7B351004A3361 /* NSObject+RACSelectorSignal.m */,
+ D0E9675B1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h */,
+ D0E9675C1641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m */,
+ D0E9675D1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h */,
+ D0E9675E1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m */,
+ D0E9675F1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h */,
+ D0E967601641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m */,
+ 88442C8D16090C1500636B49 /* NSString+RACSupport.h */,
+ 88442C8E16090C1500636B49 /* NSString+RACSupport.m */,
+ D090767D17FBEADE00EB087A /* NSURLConnection+RACSupport.h */,
+ D090767E17FBEADE00EB087A /* NSURLConnection+RACSupport.m */,
+ BE527E9118636F7F006349E8 /* NSUserDefaults+RACSupport.h */,
+ BE527E9218636F7F006349E8 /* NSUserDefaults+RACSupport.m */,
+ );
+ name = "Foundation Support";
+ sourceTree = "<group>";
+ };
+ 88CDF7B015000FCE00163A9F = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7C515000FCE00163A9F /* ReactiveCocoa */,
+ 88CDF7E215000FCF00163A9F /* ReactiveCocoaTests */,
+ 88CDF7BE15000FCE00163A9F /* Frameworks */,
+ D094E45117775B1000906BF7 /* Configuration */,
+ 88CDF7BC15000FCE00163A9F /* Products */,
+ );
+ sourceTree = "<group>";
+ usesTabs = 1;
+ };
+ 88CDF7BC15000FCE00163A9F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7DC15000FCF00163A9F /* ReactiveCocoaTests.octest */,
+ 88037F8315056328001A5B19 /* ReactiveCocoa.framework */,
+ 88F440AB153DAC820097B4C3 /* libReactiveCocoa-iOS.a */,
+ 5FAF5223174D4C2000CAC810 /* ReactiveCocoaTests-iOS.octest */,
+ 1860F412177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost.app */,
+ 1860F430177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests.octest */,
+ D05AD39417F2D56F0080895B /* libReactiveCocoa-Mac.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 88CDF7BE15000FCE00163A9F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ D40D7AAC18E22B7E0065BB70 /* libSpecta.a */,
+ D40D7AB018E22BF60065BB70 /* libSpecta-iOS.a */,
+ D40D7AAA18E22B5E0065BB70 /* libExpecta.a */,
+ D40D7AAE18E22BE30065BB70 /* libExpecta-iOS.a */,
+ D094E44417775ACD00906BF7 /* libextobjc */,
+ 1860F413177C91B500C7B3C9 /* UIKit.framework */,
+ 1860F415177C91B500C7B3C9 /* Foundation.framework */,
+ 1860F417177C91B500C7B3C9 /* CoreGraphics.framework */,
+ 1860F431177C91B500C7B3C9 /* SenTestingKit.framework */,
+ D05AD39517F2D5700080895B /* Cocoa.framework */,
+ D05AD3A317F2D5700080895B /* XCTest.framework */,
+ 88CDF7C115000FCE00163A9F /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 88CDF7C115000FCE00163A9F /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7BF15000FCE00163A9F /* Cocoa.framework */,
+ 88CDF7C315000FCE00163A9F /* CoreData.framework */,
+ 5FAF5288174E9CD200CAC810 /* CoreGraphics.framework */,
+ 88CDF7C415000FCE00163A9F /* Foundation.framework */,
+ 88CDF7DD15000FCF00163A9F /* SenTestingKit.framework */,
+ 5FD7DC7B174F9EEB008710B4 /* UIKit.framework */,
+ D05AD39717F2D5700080895B /* AppKit.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 88CDF7C515000FCE00163A9F /* ReactiveCocoa */ = {
+ isa = PBXGroup;
+ children = (
+ 88037F8C15056328001A5B19 /* ReactiveCocoa.h */,
+ 88DA308C15071C4C00C19D0F /* Core */,
+ 889C04BB155DA37600F19F0C /* Foundation Support */,
+ 88DA309415071C5F00C19D0F /* AppKit Support */,
+ 88F44257153DC0100097B4C3 /* UIKit Support */,
+ 88977C5915129AB200A09EC5 /* KVO + Bindings */,
+ A1FCC36F15675466008C9686 /* Objective-C Runtime */,
+ 88CDF7C615000FCE00163A9F /* Supporting Files */,
+ );
+ path = ReactiveCocoa;
+ sourceTree = "<group>";
+ };
+ 88CDF7C615000FCE00163A9F /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7C715000FCE00163A9F /* ReactiveCocoa-Info.plist */,
+ 88CDF7C815000FCE00163A9F /* InfoPlist.strings */,
+ 88CDF7CD15000FCE00163A9F /* ReactiveCocoa-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 88CDF7E215000FCF00163A9F /* ReactiveCocoaTests */ = {
+ isa = PBXGroup;
+ children = (
+ 5FAF5260174D4D7100CAC810 /* iOS */,
+ 5FAF525F174D4D6100CAC810 /* OS X */,
+ 88CDF7E315000FCF00163A9F /* Supporting Files */,
+ 5F773DEF169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m */,
+ D0700F4B1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m */,
+ 6E58405E16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m */,
+ 8801E7501644BDE200A155FE /* NSObjectRACLiftingSpec.m */,
+ 1E89337F171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.h */,
+ 1E893380171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m */,
+ 8851A38A16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m */,
+ 880D7A6516F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m */,
+ 5FDC350E1736F81800792E52 /* NSStringRACKeyPathUtilitiesSpec.m */,
+ D090768917FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m */,
+ BE527E9E1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m */,
+ D0870C6E16884A0600D0E11D /* RACBacktraceSpec.m */,
+ 5F7EFECC168FBC4B0037E500 /* RACChannelExamples.h */,
+ 5F7EFECD168FBC4B0037E500 /* RACChannelExamples.m */,
+ 5F7EFECE168FBC4B0037E500 /* RACChannelSpec.m */,
+ D075A72817BCB7E100C24FB7 /* RACControlCommandExamples.h */,
+ D075A72917BCB7E100C24FB7 /* RACControlCommandExamples.m */,
+ 886CEACC163DE669007632D1 /* RACBlockTrampolineSpec.m */,
+ 882CCA1D15F1564D00937D6E /* RACCommandSpec.m */,
+ 881E86B91669350B00667F7B /* RACCompoundDisposableSpec.m */,
+ D0A0E225176A84DA007273ED /* RACDisposableSpec.m */,
+ D077A171169B79A900057BB1 /* RACEventSpec.m */,
+ 5F2447AC167E87C50062180C /* RACKVOChannelSpec.m */,
+ D041376815D2281C004BBF80 /* RACKVOWrapperSpec.m */,
+ 88C5A02816924BFC0045EF05 /* RACMulticastConnectionSpec.m */,
+ D0EDE76516968AB10072A780 /* RACPropertySignalExamples.h */,
+ D0EDE76616968AB10072A780 /* RACPropertySignalExamples.m */,
+ 8803C010166732BA00C36839 /* RACSchedulerSpec.m */,
+ D0C70F8F164337A2007027B4 /* RACSequenceAdditionsSpec.m */,
+ D0C70F91164337E3007027B4 /* RACSequenceExamples.h */,
+ D0C70F92164337E3007027B4 /* RACSequenceExamples.m */,
+ D0D487051642651400DD7605 /* RACSequenceSpec.m */,
+ 8820937B1501C8A600796685 /* RACSignalSpec.m */,
+ 8884DD6A1756AD3600F6C379 /* RACSignalStartExamples.h */,
+ 8884DD641756ACF600F6C379 /* RACSignalStartExamples.m */,
+ D0487AB1164314430085D890 /* RACStreamExamples.h */,
+ D0487AB2164314430085D890 /* RACStreamExamples.m */,
+ 880D7A6716F7BCC7004A3361 /* RACSubclassObject.h */,
+ 880D7A6816F7BCC7004A3361 /* RACSubclassObject.m */,
+ 889D0A7F15974B2A00F833E3 /* RACSubjectSpec.m */,
+ D0C70EC416659333005AAD03 /* RACSubscriberExamples.h */,
+ D0C70EC516659333005AAD03 /* RACSubscriberExamples.m */,
+ D0C70EC7166595AD005AAD03 /* RACSubscriberSpec.m */,
+ 88FC735A16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m */,
+ 88302C2D1762C180003633BD /* RACTargetQueueSchedulerSpec.m */,
+ 88302BFB1762A9E6003633BD /* RACTestExampleScheduler.h */,
+ 88302BFC1762A9E6003633BD /* RACTestExampleScheduler.m */,
+ 88442A321608A9AD00636B49 /* RACTestObject.h */,
+ 88442A331608A9AD00636B49 /* RACTestObject.m */,
+ D02221611678910900DBD031 /* RACTupleSpec.m */,
+ 8803C010166732BA00C36839 /* RACSchedulerSpec.m */,
+ D028DB7C179E591E00D1042F /* RACSerialDisposableSpec.m */,
+ D07200241788C57200987F70 /* RACTestSchedulerSpec.m */,
+ );
+ path = ReactiveCocoaTests;
+ sourceTree = "<group>";
+ };
+ 88CDF7E315000FCF00163A9F /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7E415000FCF00163A9F /* ReactiveCocoaTests-Info.plist */,
+ 88CDF7E515000FCF00163A9F /* InfoPlist.strings */,
+ D01DB9AE166819B9003E8F7F /* ReactiveCocoaTests-Prefix.pch */,
+ D090768C17FBED2E00EB087A /* test-data.json */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 88DA308C15071C4C00C19D0F /* Core */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF7FA150019CA00163A9F /* RACSubscriber.h */,
+ D0FAEC02176AEEE600D3C1A7 /* RACSubscriber+Private.h */,
+ 88CDF7FB150019CA00163A9F /* RACSubscriber.m */,
+ D0A0E22C176A8CD6007273ED /* RACPassthroughSubscriber.h */,
+ D0A0E22D176A8CD6007273ED /* RACPassthroughSubscriber.m */,
+ 888439A11634E10D00DED0DB /* RACBlockTrampoline.h */,
+ 888439A21634E10D00DED0DB /* RACBlockTrampoline.m */,
+ 881B37CA152260BF0079220B /* RACUnit.h */,
+ 881B37CB152260BF0079220B /* RACUnit.m */,
+ 88B76F8C153726B00053EAE2 /* RACTuple.h */,
+ 88B76F8D153726B00053EAE2 /* RACTuple.m */,
+ D02538A115E2D7FB005BACB8 /* RACBacktrace.h */,
+ D0DFBCCD15DD6D40009DADB3 /* RACBacktrace.m */,
+ 88FC735316114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.h */,
+ 88FC735416114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m */,
+ 6E58405316F22D7500F588A6 /* NSObject+RACDeallocating.h */,
+ 6E58405416F22D7500F588A6 /* NSObject+RACDeallocating.m */,
+ 886CEAE0163DE942007632D1 /* NSObject+RACLifting.h */,
+ 886CEAE1163DE942007632D1 /* NSObject+RACLifting.m */,
+ 887ACDA5165878A7009190AD /* NSInvocation+RACTypeParsing.h */,
+ 887ACDA6165878A7009190AD /* NSInvocation+RACTypeParsing.m */,
+ D0D486FF1642550100DD7605 /* RACStream.h */,
+ D07E9489179DD21E00A6F609 /* RACStream+Private.h */,
+ D0D487001642550100DD7605 /* RACStream.m */,
+ D0D486FB164253B600DD7605 /* Signals */,
+ D0D486FD164253D500DD7605 /* Disposables */,
+ D0D486FE164253E100DD7605 /* Commands */,
+ D0E967561641EF8200FCFF06 /* Sequences */,
+ D0087C1B16705C5600679459 /* Schedulers */,
+ );
+ name = Core;
+ sourceTree = "<group>";
+ };
+ 88DA309415071C5F00C19D0F /* AppKit Support */ = {
+ isa = PBXGroup;
+ children = (
+ 882093E61501E6CB00796685 /* NSControl+RACCommandSupport.h */,
+ 882093E71501E6CB00796685 /* NSControl+RACCommandSupport.m */,
+ D0A0B03916EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h */,
+ D0A0B03A16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m */,
+ 88F440D1153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h */,
+ 88F440D2153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m */,
+ D0A0B01316EAA3D100C47593 /* NSText+RACSignalSupport.h */,
+ D0A0B01416EAA3D100C47593 /* NSText+RACSignalSupport.m */,
+ );
+ name = "AppKit Support";
+ sourceTree = "<group>";
+ };
+ 88F44257153DC0100097B4C3 /* UIKit Support */ = {
+ isa = PBXGroup;
+ children = (
+ A1FCC3761567DED0008C9686 /* RACDelegateProxy.h */,
+ A1FCC3771567DED0008C9686 /* RACDelegateProxy.m */,
+ 557A4B58177648C7008EF796 /* UIActionSheet+RACSignalSupport.h */,
+ 557A4B59177648C7008EF796 /* UIActionSheet+RACSignalSupport.m */,
+ ACB0EAF21797DDD400942FFC /* UIAlertView+RACSignalSupport.h */,
+ ACB0EAF11797DDD400942FFC /* UIAlertView+RACSignalSupport.m */,
+ 27A887C71703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.h */,
+ 27A887C81703DB4F00040001 /* UIBarButtonItem+RACCommandSupport.m */,
+ 5EE9A7911760D61300EAF5A2 /* UIButton+RACCommandSupport.h */,
+ 5EE9A7921760D61300EAF5A2 /* UIButton+RACCommandSupport.m */,
+ 1668027817FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.h */,
+ 1668027917FE75E900C724B4 /* UICollectionReusableView+RACSignalSupport.m */,
+ 88F4425F153DC0450097B4C3 /* UIControl+RACSignalSupport.h */,
+ 88F44260153DC0450097B4C3 /* UIControl+RACSignalSupport.m */,
+ 5F016DF217B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.h */,
+ 5F016DF317B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.m */,
+ 5F70B2AD17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.h */,
+ 5F70B2AE17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.m */,
+ 1EC06B15173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.h */,
+ 1EC06B16173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.m */,
+ 554D9E5B181064E200F21262 /* UIRefreshControl+RACCommandSupport.h */,
+ 554D9E5C181064E200F21262 /* UIRefreshControl+RACCommandSupport.m */,
+ 5F70B2B617AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.h */,
+ 5F70B2B717AB1856009AEDF9 /* UISegmentedControl+RACSignalSupport.m */,
+ 5F70B2B817AB1856009AEDF9 /* UISlider+RACSignalSupport.h */,
+ 5F70B2B917AB1856009AEDF9 /* UISlider+RACSignalSupport.m */,
+ 5F70B2BA17AB1857009AEDF9 /* UIStepper+RACSignalSupport.h */,
+ 5F70B2BB17AB1857009AEDF9 /* UIStepper+RACSignalSupport.m */,
+ 5F70B2BC17AB1857009AEDF9 /* UISwitch+RACSignalSupport.h */,
+ 5F70B2BD17AB1857009AEDF9 /* UISwitch+RACSignalSupport.m */,
+ D028DB85179E616700D1042F /* UITableViewCell+RACSignalSupport.h */,
+ D028DB86179E616700D1042F /* UITableViewCell+RACSignalSupport.m */,
+ CD11C6F118714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.h */,
+ CD11C6F218714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.m */,
+ 88F44264153DCAC50097B4C3 /* UITextField+RACSignalSupport.h */,
+ 88F44265153DCAC50097B4C3 /* UITextField+RACSignalSupport.m */,
+ A1FCC27215666AA3008C9686 /* UITextView+RACSignalSupport.h */,
+ A1FCC27315666AA3008C9686 /* UITextView+RACSignalSupport.m */,
+ );
+ name = "UIKit Support";
+ sourceTree = "<group>";
+ };
+ A1FCC36F15675466008C9686 /* Objective-C Runtime */ = {
+ isa = PBXGroup;
+ children = (
+ A1FCC370156754A7008C9686 /* RACObjCRuntime.h */,
+ A1FCC371156754A7008C9686 /* RACObjCRuntime.m */,
+ );
+ name = "Objective-C Runtime";
+ sourceTree = "<group>";
+ };
+ D0087C1B16705C5600679459 /* Schedulers */ = {
+ isa = PBXGroup;
+ children = (
+ 88E2C6B2153C771C00C7493C /* RACScheduler.h */,
+ 886D98581667C86D00F22541 /* RACScheduler+Private.h */,
+ 88E2C6B3153C771C00C7493C /* RACScheduler.m */,
+ 881E87AA16695C5600667F7B /* RACQueueScheduler.h */,
+ 882D07201761521B009EDA69 /* RACQueueScheduler+Subclass.h */,
+ 881E87AB16695C5600667F7B /* RACQueueScheduler.m */,
+ 882D071717614FA7009EDA69 /* RACTargetQueueScheduler.h */,
+ 882D071817614FA7009EDA69 /* RACTargetQueueScheduler.m */,
+ 881E87B016695EDF00667F7B /* RACImmediateScheduler.h */,
+ 881E87B116695EDF00667F7B /* RACImmediateScheduler.m */,
+ 881E87C21669635F00667F7B /* RACSubscriptionScheduler.h */,
+ 881E87C31669636000667F7B /* RACSubscriptionScheduler.m */,
+ D00930771788AB7B00EE7E8B /* RACTestScheduler.h */,
+ D00930781788AB7B00EE7E8B /* RACTestScheduler.m */,
+ );
+ name = Schedulers;
+ sourceTree = "<group>";
+ };
+ D013A3EC1807B9260072B6CE /* Private */ = {
+ isa = PBXGroup;
+ children = (
+ D013A3ED1807B9690072B6CE /* RACDynamicSignal.h */,
+ D013A3EE1807B9690072B6CE /* RACDynamicSignal.m */,
+ D013A3DC1807B7450072B6CE /* RACEmptySignal.h */,
+ D013A3DD1807B7450072B6CE /* RACEmptySignal.m */,
+ D013A3D41807B5ED0072B6CE /* RACErrorSignal.h */,
+ D013A3D51807B5ED0072B6CE /* RACErrorSignal.m */,
+ D013A3E41807B7C30072B6CE /* RACReturnSignal.h */,
+ D013A3E51807B7C30072B6CE /* RACReturnSignal.m */,
+ );
+ name = Private;
+ sourceTree = "<group>";
+ };
+ D094E44417775ACD00906BF7 /* libextobjc */ = {
+ isa = PBXGroup;
+ children = (
+ D094E44517775AF200906BF7 /* EXTKeyPathCoding.h */,
+ D05F9D3317984EC000FD7982 /* EXTRuntimeExtensions.h */,
+ D05F9D3417984EC000FD7982 /* EXTRuntimeExtensions.m */,
+ D094E44617775AF200906BF7 /* EXTScope.h */,
+ D094E44817775AF200906BF7 /* metamacros.h */,
+ );
+ name = libextobjc;
+ path = ReactiveCocoa/extobjc;
+ sourceTree = "<group>";
+ };
+ D094E45117775B1000906BF7 /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ D094E45217775B1000906BF7 /* Base */,
+ D094E45C17775B1000906BF7 /* iOS */,
+ D094E46017775B1000906BF7 /* Mac OS X */,
+ D094E46617775B1000906BF7 /* README.md */,
+ );
+ name = Configuration;
+ path = ../external/xcconfigs;
+ sourceTree = "<group>";
+ };
+ D094E45217775B1000906BF7 /* Base */ = {
+ isa = PBXGroup;
+ children = (
+ D094E45317775B1000906BF7 /* Common.xcconfig */,
+ D094E45417775B1000906BF7 /* Configurations */,
+ D094E45917775B1000906BF7 /* Targets */,
+ );
+ path = Base;
+ sourceTree = "<group>";
+ };
+ D094E45417775B1000906BF7 /* Configurations */ = {
+ isa = PBXGroup;
+ children = (
+ D094E45517775B1000906BF7 /* Debug.xcconfig */,
+ D094E45617775B1000906BF7 /* Profile.xcconfig */,
+ D094E45717775B1000906BF7 /* Release.xcconfig */,
+ D094E45817775B1000906BF7 /* Test.xcconfig */,
+ );
+ path = Configurations;
+ sourceTree = "<group>";
+ };
+ D094E45917775B1000906BF7 /* Targets */ = {
+ isa = PBXGroup;
+ children = (
+ D094E45A17775B1000906BF7 /* Application.xcconfig */,
+ D094E45B17775B1000906BF7 /* StaticLibrary.xcconfig */,
+ );
+ path = Targets;
+ sourceTree = "<group>";
+ };
+ D094E45C17775B1000906BF7 /* iOS */ = {
+ isa = PBXGroup;
+ children = (
+ D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */,
+ D094E45E17775B1000906BF7 /* iOS-Base.xcconfig */,
+ D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */,
+ );
+ path = iOS;
+ sourceTree = "<group>";
+ };
+ D094E46017775B1000906BF7 /* Mac OS X */ = {
+ isa = PBXGroup;
+ children = (
+ D094E46117775B1000906BF7 /* Mac-Application.xcconfig */,
+ D094E46217775B1000906BF7 /* Mac-Base.xcconfig */,
+ D094E46317775B1000906BF7 /* Mac-DynamicLibrary.xcconfig */,
+ D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */,
+ D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */,
+ );
+ path = "Mac OS X";
+ sourceTree = "<group>";
+ };
+ D0D486FB164253B600DD7605 /* Signals */ = {
+ isa = PBXGroup;
+ children = (
+ 88CDF80415001CA800163A9F /* RACSignal.h */,
+ 88977C3D1512914A00A09EC5 /* RACSignal.m */,
+ D03525D317E2EBC90099CBAB /* RACSignalProvider.d */,
+ D0D910CC15F915BD00AD2DDA /* RACSignal+Operations.h */,
+ D0D910CD15F915BD00AD2DDA /* RACSignal+Operations.m */,
+ D077A16B169B740200057BB1 /* RACEvent.h */,
+ D077A16C169B740200057BB1 /* RACEvent.m */,
+ 88C5A0231692460A0045EF05 /* RACMulticastConnection.h */,
+ 88F5870515361C170084BD32 /* RACMulticastConnection+Private.h */,
+ 88C5A025169246140045EF05 /* RACMulticastConnection.m */,
+ 886F70281551CF920045D68B /* RACGroupedSignal.h */,
+ 886F70291551CF920045D68B /* RACGroupedSignal.m */,
+ D013A3EC1807B9260072B6CE /* Private */,
+ D0D486FC164253C400DD7605 /* Subjects */,
+ );
+ name = Signals;
+ sourceTree = "<group>";
+ };
+ D0D486FC164253C400DD7605 /* Subjects */ = {
+ isa = PBXGroup;
+ children = (
+ 880B9174150B09190008488E /* RACSubject.h */,
+ 880B9175150B09190008488E /* RACSubject.m */,
+ 88D4AB3C1510F6C30011494F /* RACReplaySubject.h */,
+ 88D4AB3D1510F6C30011494F /* RACReplaySubject.m */,
+ 883A84D81513964B006DB4C7 /* RACBehaviorSubject.h */,
+ 883A84D91513964B006DB4C7 /* RACBehaviorSubject.m */,
+ );
+ name = Subjects;
+ sourceTree = "<group>";
+ };
+ D0D486FD164253D500DD7605 /* Disposables */ = {
+ isa = PBXGroup;
+ children = (
+ 883A84DD1513B5EC006DB4C7 /* RACDisposable.h */,
+ 883A84DE1513B5EC006DB4C7 /* RACDisposable.m */,
+ 884476E2152367D100958F44 /* RACScopedDisposable.h */,
+ 884476E3152367D100958F44 /* RACScopedDisposable.m */,
+ 881E86A01669304700667F7B /* RACCompoundDisposable.h */,
+ 881E86A11669304700667F7B /* RACCompoundDisposable.m */,
+ D020F3DA17F6A3E40092BED2 /* RACCompoundDisposableProvider.d */,
+ D028DB72179E53CB00D1042F /* RACSerialDisposable.h */,
+ D028DB73179E53CB00D1042F /* RACSerialDisposable.m */,
+ );
+ name = Disposables;
+ sourceTree = "<group>";
+ };
+ D0D486FE164253E100DD7605 /* Commands */ = {
+ isa = PBXGroup;
+ children = (
+ 882093E91501E6EE00796685 /* RACCommand.h */,
+ 882093EA1501E6EE00796685 /* RACCommand.m */,
+ );
+ name = Commands;
+ sourceTree = "<group>";
+ };
+ D0E967561641EF8200FCFF06 /* Sequences */ = {
+ isa = PBXGroup;
+ children = (
+ D0E967671641EF9C00FCFF06 /* RACSequence.h */,
+ D0E967681641EF9C00FCFF06 /* RACSequence.m */,
+ D0E967611641EF9C00FCFF06 /* RACArraySequence.h */,
+ D0E967621641EF9C00FCFF06 /* RACArraySequence.m */,
+ D0E967631641EF9C00FCFF06 /* RACDynamicSequence.h */,
+ D0E967641641EF9C00FCFF06 /* RACDynamicSequence.m */,
+ 5F9743F51694A2460024EB82 /* RACEagerSequence.h */,
+ 5F9743F61694A2460024EB82 /* RACEagerSequence.m */,
+ D0E967651641EF9C00FCFF06 /* RACEmptySequence.h */,
+ D0E967661641EF9C00FCFF06 /* RACEmptySequence.m */,
+ 7479F6E3186177D200575CDB /* RACIndexSetSequence.h */,
+ 7479F6E4186177D200575CDB /* RACIndexSetSequence.m */,
+ D0E967691641EF9C00FCFF06 /* RACStringSequence.h */,
+ D0E9676A1641EF9C00FCFF06 /* RACStringSequence.m */,
+ D0EE2849164D906B006954A4 /* RACSignalSequence.h */,
+ D0EE284A164D906B006954A4 /* RACSignalSequence.m */,
+ D0307EDB1731AAE100D83211 /* RACTupleSequence.h */,
+ D0307EDC1731AAE100D83211 /* RACTupleSequence.m */,
+ D07CD7141731BA3900DE2394 /* RACUnarySequence.h */,
+ D07CD7151731BA3900DE2394 /* RACUnarySequence.m */,
+ );
+ name = Sequences;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 88037F8015056328001A5B19 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 882D071917614FA7009EDA69 /* RACTargetQueueScheduler.h in Headers */,
+ 881E87AC16695C5600667F7B /* RACQueueScheduler.h in Headers */,
+ 88037FB81505645C001A5B19 /* ReactiveCocoa.h in Headers */,
+ 88037FBB1505646C001A5B19 /* NSObject+RACPropertySubscribing.h in Headers */,
+ 88037FBC1505646C001A5B19 /* RACSignal.h in Headers */,
+ 88037FBE1505646C001A5B19 /* RACSubscriber.h in Headers */,
+ 88037FC11505646C001A5B19 /* RACCommand.h in Headers */,
+ 88037FC21505646C001A5B19 /* NSControl+RACCommandSupport.h in Headers */,
+ D028DB74179E53CB00D1042F /* RACSerialDisposable.h in Headers */,
+ 880B9176150B09190008488E /* RACSubject.h in Headers */,
+ D090767F17FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */,
+ 88F440D3153DADEA0097B4C3 /* NSObject+RACAppKitBindings.h in Headers */,
+ 88D4AB3E1510F6C30011494F /* RACReplaySubject.h in Headers */,
+ 883A84DA1513964B006DB4C7 /* RACBehaviorSubject.h in Headers */,
+ 883A84DF1513B5EC006DB4C7 /* RACDisposable.h in Headers */,
+ 886F702A1551CF920045D68B /* RACGroupedSignal.h in Headers */,
+ 881B37CC152260BF0079220B /* RACUnit.h in Headers */,
+ 884476E4152367D100958F44 /* RACScopedDisposable.h in Headers */,
+ 88E2C6B4153C771C00C7493C /* RACScheduler.h in Headers */,
+ 88B76F8E153726B00053EAE2 /* RACTuple.h in Headers */,
+ 886CEAE2163DE942007632D1 /* NSObject+RACLifting.h in Headers */,
+ D0D910CE15F915BD00AD2DDA /* RACSignal+Operations.h in Headers */,
+ D0E9676B1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.h in Headers */,
+ D0E9676F1641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.h in Headers */,
+ D0E967731641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.h in Headers */,
+ D0E967771641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.h in Headers */,
+ D0E9677B1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.h in Headers */,
+ D0E9678B1641EF9C00FCFF06 /* RACSequence.h in Headers */,
+ 88A0B6D3165B2B77005DE8F3 /* RACSubscriptingAssignmentTrampoline.h in Headers */,
+ D0D487011642550100DD7605 /* RACStream.h in Headers */,
+ 88C5A0241692460A0045EF05 /* RACMulticastConnection.h in Headers */,
+ 881E86A21669304800667F7B /* RACCompoundDisposable.h in Headers */,
+ 881E87C41669636000667F7B /* RACSubscriptionScheduler.h in Headers */,
+ 5F45A885168CFA3E00B58A2B /* RACKVOChannel.h in Headers */,
+ 880D7A5A16F7B351004A3361 /* NSObject+RACSelectorSignal.h in Headers */,
+ 6E58405516F22D7500F588A6 /* NSObject+RACDeallocating.h in Headers */,
+ 5F6FE8531692568A00A8D7A6 /* RACChannel.h in Headers */,
+ 882D072117615381009EDA69 /* RACQueueScheduler+Subclass.h in Headers */,
+ D005A259169A3B7D00A9D2DB /* RACBacktrace.h in Headers */,
+ BE527E9318636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */,
+ 5F773DEA169B46670023069D /* NSEnumerator+RACSequenceAdditions.h in Headers */,
+ D077A16D169B740200057BB1 /* RACEvent.h in Headers */,
+ D0A0B01516EAA3D100C47593 /* NSText+RACSignalSupport.h in Headers */,
+ D0A0B03B16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.h in Headers */,
+ D094E44917775AF200906BF7 /* EXTKeyPathCoding.h in Headers */,
+ D094E44B17775AF200906BF7 /* EXTScope.h in Headers */,
+ D094E44F17775AF200906BF7 /* metamacros.h in Headers */,
+ D00930791788AB7B00EE7E8B /* RACTestScheduler.h in Headers */,
+ 74F17318186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */,
+ 55C39DEC17F1EC84006DC60C /* NSData+RACSupport.h in Headers */,
+ 55C39DED17F1EC84006DC60C /* NSFileHandle+RACSupport.h in Headers */,
+ 55C39DEE17F1EC84006DC60C /* NSNotificationCenter+RACSupport.h in Headers */,
+ 55C39DEF17F1EC84006DC60C /* NSString+RACSupport.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88F440A9153DAC820097B4C3 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 880D7A5B16F7B351004A3361 /* NSObject+RACSelectorSignal.h in Headers */,
+ CD11C6F318714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */,
+ 5F45A886168CFA3E00B58A2B /* RACKVOChannel.h in Headers */,
+ 554D9E5D181064E200F21262 /* UIRefreshControl+RACCommandSupport.h in Headers */,
+ 5F6FE8541692568A00A8D7A6 /* RACChannel.h in Headers */,
+ D08FF264169A32D100743C6D /* ReactiveCocoa.h in Headers */,
+ D08FF265169A32DC00743C6D /* RACSubscriber.h in Headers */,
+ D08FF267169A330000743C6D /* RACUnit.h in Headers */,
+ D08FF268169A330000743C6D /* RACTuple.h in Headers */,
+ D08FF269169A330000743C6D /* RACBacktrace.h in Headers */,
+ D08FF26A169A330000743C6D /* RACSubscriptingAssignmentTrampoline.h in Headers */,
+ 74F17319186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */,
+ D08FF26C169A331A00743C6D /* RACStream.h in Headers */,
+ D08FF26D169A331A00743C6D /* RACSignal.h in Headers */,
+ D08FF26E169A331A00743C6D /* RACSignal+Operations.h in Headers */,
+ D08FF26F169A331A00743C6D /* RACMulticastConnection.h in Headers */,
+ D08FF270169A331A00743C6D /* RACGroupedSignal.h in Headers */,
+ D08FF271169A331A00743C6D /* RACSubject.h in Headers */,
+ D08FF272169A331A00743C6D /* RACReplaySubject.h in Headers */,
+ D08FF273169A331A00743C6D /* RACBehaviorSubject.h in Headers */,
+ D08FF274169A331A00743C6D /* RACDisposable.h in Headers */,
+ D08FF275169A331A00743C6D /* RACScopedDisposable.h in Headers */,
+ D08FF276169A331A00743C6D /* RACCompoundDisposable.h in Headers */,
+ D08FF277169A331B00743C6D /* RACCommand.h in Headers */,
+ D08FF278169A331B00743C6D /* RACSequence.h in Headers */,
+ D08FF27F169A331B00743C6D /* RACScheduler.h in Headers */,
+ D08FF26B169A330000743C6D /* NSObject+RACLifting.h in Headers */,
+ D08FF280169A333400743C6D /* NSArray+RACSequenceAdditions.h in Headers */,
+ D028DB75179E53CB00D1042F /* RACSerialDisposable.h in Headers */,
+ D08FF281169A333400743C6D /* NSDictionary+RACSequenceAdditions.h in Headers */,
+ D08FF282169A333400743C6D /* NSOrderedSet+RACSequenceAdditions.h in Headers */,
+ D028DB87179E616700D1042F /* UITableViewCell+RACSignalSupport.h in Headers */,
+ 1646747B17FFA0610036E30B /* UICollectionReusableView+RACSignalSupport.h in Headers */,
+ D08FF283169A333400743C6D /* NSSet+RACSequenceAdditions.h in Headers */,
+ D08FF284169A333400743C6D /* NSString+RACSequenceAdditions.h in Headers */,
+ D08FF285169A333400743C6D /* UIControl+RACSignalSupport.h in Headers */,
+ D08FF286169A333400743C6D /* UITextField+RACSignalSupport.h in Headers */,
+ D08FF287169A333400743C6D /* UITextView+RACSignalSupport.h in Headers */,
+ D08FF289169A333400743C6D /* NSObject+RACPropertySubscribing.h in Headers */,
+ 5F773DEB169B46670023069D /* NSEnumerator+RACSequenceAdditions.h in Headers */,
+ D077A16E169B740200057BB1 /* RACEvent.h in Headers */,
+ 6EA0C08216F4AEC1006EBEB2 /* NSObject+RACDeallocating.h in Headers */,
+ 27A887D21703DDEB00040001 /* UIBarButtonItem+RACCommandSupport.h in Headers */,
+ 1EC06B17173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.h in Headers */,
+ D090768017FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */,
+ 5EE9A7931760D61300EAF5A2 /* UIButton+RACCommandSupport.h in Headers */,
+ 88302C961762EC79003633BD /* RACQueueScheduler.h in Headers */,
+ BE527E9418636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */,
+ 88302C9B1762EC7E003633BD /* RACQueueScheduler+Subclass.h in Headers */,
+ 88302CA21762F62D003633BD /* RACTargetQueueScheduler.h in Headers */,
+ 557A4B5A177648C7008EF796 /* UIActionSheet+RACSignalSupport.h in Headers */,
+ ACB0EAF41797DDD400942FFC /* UIAlertView+RACSignalSupport.h in Headers */,
+ D094E44A17775AF200906BF7 /* EXTKeyPathCoding.h in Headers */,
+ D094E44C17775AF200906BF7 /* EXTScope.h in Headers */,
+ D094E45017775AF200906BF7 /* metamacros.h in Headers */,
+ D009307A1788AB7B00EE7E8B /* RACTestScheduler.h in Headers */,
+ 5F70B2AF17AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.h in Headers */,
+ 5F70B2BE17AB1857009AEDF9 /* UISegmentedControl+RACSignalSupport.h in Headers */,
+ 5F70B2C017AB1857009AEDF9 /* UISlider+RACSignalSupport.h in Headers */,
+ 5F70B2C217AB1857009AEDF9 /* UIStepper+RACSignalSupport.h in Headers */,
+ 5F70B2C417AB1857009AEDF9 /* UISwitch+RACSignalSupport.h in Headers */,
+ 55C39DF017F1EC84006DC60C /* NSData+RACSupport.h in Headers */,
+ 55C39DF117F1EC84006DC60C /* NSFileHandle+RACSupport.h in Headers */,
+ 55C39DF217F1EC84006DC60C /* NSNotificationCenter+RACSupport.h in Headers */,
+ 55C39DF317F1EC84006DC60C /* NSString+RACSupport.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D05AD39217F2D56F0080895B /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D05AD3E017F2DB1D0080895B /* RACBehaviorSubject.h in Headers */,
+ D05AD3C917F2DB100080895B /* RACSubscriptingAssignmentTrampoline.h in Headers */,
+ D05AD3C517F2DB100080895B /* RACTuple.h in Headers */,
+ 74F1731A186024A900BC937C /* NSIndexSet+RACSequenceAdditions.h in Headers */,
+ D05AD41A17F2DB6A0080895B /* NSString+RACSequenceAdditions.h in Headers */,
+ D05AD41E17F2DB6E0080895B /* NSControl+RACCommandSupport.h in Headers */,
+ D05AD3E417F2DB230080895B /* RACScopedDisposable.h in Headers */,
+ D05AD40F17F2DB6A0080895B /* NSFileHandle+RACSupport.h in Headers */,
+ D05AD40517F2DB5D0080895B /* RACTestScheduler.h in Headers */,
+ D05AD42817F2DB840080895B /* RACKVOChannel.h in Headers */,
+ D05AD3FF17F2DB5D0080895B /* RACTargetQueueScheduler.h in Headers */,
+ D05AD3E217F2DB230080895B /* RACDisposable.h in Headers */,
+ D05AD42617F2DB840080895B /* RACChannel.h in Headers */,
+ D05AD3EA17F2DB270080895B /* RACCommand.h in Headers */,
+ D05AD3DE17F2DB1D0080895B /* RACReplaySubject.h in Headers */,
+ D05AD3D817F2DB1D0080895B /* RACMulticastConnection.h in Headers */,
+ D05AD3E817F2DB230080895B /* RACSerialDisposable.h in Headers */,
+ D05AD42E17F2DB840080895B /* NSObject+RACPropertySubscribing.h in Headers */,
+ D05AD3CB17F2DB100080895B /* NSObject+RACDeallocating.h in Headers */,
+ D05AD3D417F2DB1D0080895B /* RACSignal+Operations.h in Headers */,
+ D05AD41417F2DB6A0080895B /* NSObject+RACSelectorSignal.h in Headers */,
+ D05AD3FC17F2DB5D0080895B /* RACQueueScheduler.h in Headers */,
+ D05AD42017F2DB6E0080895B /* NSControl+RACTextSignalSupport.h in Headers */,
+ D05AD3FD17F2DB5D0080895B /* RACQueueScheduler+Subclass.h in Headers */,
+ D05AD42217F2DB6E0080895B /* NSObject+RACAppKitBindings.h in Headers */,
+ BE527E9518636F7F006349E8 /* NSUserDefaults+RACSupport.h in Headers */,
+ D05AD3DA17F2DB1D0080895B /* RACGroupedSignal.h in Headers */,
+ D05AD41617F2DB6A0080895B /* NSOrderedSet+RACSequenceAdditions.h in Headers */,
+ D05AD3E617F2DB230080895B /* RACCompoundDisposable.h in Headers */,
+ D05AD40B17F2DB6A0080895B /* NSDictionary+RACSequenceAdditions.h in Headers */,
+ D05AD3D617F2DB1D0080895B /* RACEvent.h in Headers */,
+ D05AD3D017F2DB100080895B /* RACStream.h in Headers */,
+ D05AD3DC17F2DB1D0080895B /* RACSubject.h in Headers */,
+ D05AD41C17F2DB6A0080895B /* NSString+RACSupport.h in Headers */,
+ D05AD42417F2DB6E0080895B /* NSText+RACSignalSupport.h in Headers */,
+ D05AD3C017F2DA300080895B /* RACSubscriber.h in Headers */,
+ D05AD40717F2DB6A0080895B /* NSArray+RACSequenceAdditions.h in Headers */,
+ D090768117FBEADE00EB087A /* NSURLConnection+RACSupport.h in Headers */,
+ D05AD3C717F2DB100080895B /* RACBacktrace.h in Headers */,
+ D05AD3EE17F2DB4F0080895B /* RACSequence.h in Headers */,
+ D05AD3D217F2DB1D0080895B /* RACSignal.h in Headers */,
+ D05AD3CD17F2DB100080895B /* NSObject+RACLifting.h in Headers */,
+ D05AD40D17F2DB6A0080895B /* NSEnumerator+RACSequenceAdditions.h in Headers */,
+ D05AD3FA17F2DB5D0080895B /* RACScheduler.h in Headers */,
+ D05AD40317F2DB5D0080895B /* RACSubscriptionScheduler.h in Headers */,
+ D05AD41817F2DB6A0080895B /* NSSet+RACSequenceAdditions.h in Headers */,
+ D05AD41117F2DB6A0080895B /* NSNotificationCenter+RACSupport.h in Headers */,
+ D05AD40917F2DB6A0080895B /* NSData+RACSupport.h in Headers */,
+ D05AD3C317F2DB100080895B /* RACUnit.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 1860F411177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1860F44C177C91B500C7B3C9 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS-UIKitTestHost" */;
+ buildPhases = (
+ 1860F40E177C91B500C7B3C9 /* Sources */,
+ 1860F40F177C91B500C7B3C9 /* Frameworks */,
+ 1860F410177C91B500C7B3C9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ D0F117C3179F0A91006CE68F /* PBXTargetDependency */,
+ );
+ name = "ReactiveCocoa-iOS-UIKitTestHost";
+ productName = "ReactiveCocoa-iOS-UIKitTestHost";
+ productReference = 1860F412177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 1860F42F177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1860F44D177C91B500C7B3C9 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS-UIKitTestHostTests" */;
+ buildPhases = (
+ 1860F42B177C91B500C7B3C9 /* Sources */,
+ 1860F42C177C91B500C7B3C9 /* Frameworks */,
+ 1860F42D177C91B500C7B3C9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 1860F436177C91B500C7B3C9 /* PBXTargetDependency */,
+ );
+ name = "ReactiveCocoa-iOS-UIKitTestHostTests";
+ productName = "ReactiveCocoa-iOS-UIKitTestHostTests";
+ productReference = 1860F430177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ 5FAF5222174D4C2000CAC810 /* ReactiveCocoaTests-iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5FAF523D174D4C2000CAC810 /* Build configuration list for PBXNativeTarget "ReactiveCocoaTests-iOS" */;
+ buildPhases = (
+ 5FAF521E174D4C2000CAC810 /* Sources */,
+ 5FAF521F174D4C2000CAC810 /* Frameworks */,
+ 5FAF5220174D4C2000CAC810 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ D0ED9DB617501806003859A6 /* PBXTargetDependency */,
+ );
+ name = "ReactiveCocoaTests-iOS";
+ productName = "ReactiveCocoaTests-iOS";
+ productReference = 5FAF5223174D4C2000CAC810 /* ReactiveCocoaTests-iOS.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ 88037F8215056328001A5B19 /* ReactiveCocoa */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 88037F8F15056328001A5B19 /* Build configuration list for PBXNativeTarget "ReactiveCocoa" */;
+ buildPhases = (
+ 88037F7E15056328001A5B19 /* Sources */,
+ 88037F7F15056328001A5B19 /* Frameworks */,
+ 88037F8015056328001A5B19 /* Headers */,
+ 88037F8115056328001A5B19 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ReactiveCocoa;
+ productName = ReactiveCocoa;
+ productReference = 88037F8315056328001A5B19 /* ReactiveCocoa.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 88CDF7DB15000FCF00163A9F /* ReactiveCocoaTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 88CDF7F015000FCF00163A9F /* Build configuration list for PBXNativeTarget "ReactiveCocoaTests" */;
+ buildPhases = (
+ 88CDF7D715000FCF00163A9F /* Sources */,
+ 88CDF7D815000FCF00163A9F /* Frameworks */,
+ 88CDF7D915000FCF00163A9F /* Resources */,
+ 8820937F1501C94E00796685 /* Copy Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 88037FDB150564E9001A5B19 /* PBXTargetDependency */,
+ );
+ name = ReactiveCocoaTests;
+ productName = GHObservableTests;
+ productReference = 88CDF7DC15000FCF00163A9F /* ReactiveCocoaTests.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ 88F440AA153DAC820097B4C3 /* ReactiveCocoa-iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 88F440B3153DAC820097B4C3 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS" */;
+ buildPhases = (
+ 88F440A7153DAC820097B4C3 /* Sources */,
+ 88F440A8153DAC820097B4C3 /* Frameworks */,
+ 88F440A9153DAC820097B4C3 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "ReactiveCocoa-iOS";
+ productName = ReactiveCocoaLib;
+ productReference = 88F440AB153DAC820097B4C3 /* libReactiveCocoa-iOS.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ D05AD39317F2D56F0080895B /* ReactiveCocoa-Mac */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D05AD3BD17F2D5710080895B /* Build configuration list for PBXNativeTarget "ReactiveCocoa-Mac" */;
+ buildPhases = (
+ D05AD39017F2D56F0080895B /* Sources */,
+ D05AD39117F2D56F0080895B /* Frameworks */,
+ D05AD39217F2D56F0080895B /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "ReactiveCocoa-Mac";
+ productName = "ReactiveCocoa-Mac";
+ productReference = D05AD39417F2D56F0080895B /* libReactiveCocoa-Mac.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 88CDF7B215000FCE00163A9F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = RAC;
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = "GitHub, Inc.";
+ };
+ buildConfigurationList = 88CDF7B515000FCE00163A9F /* Build configuration list for PBXProject "ReactiveCocoa" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 88CDF7B015000FCE00163A9F;
+ productRefGroup = 88CDF7BC15000FCE00163A9F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 88037F8215056328001A5B19 /* ReactiveCocoa */,
+ 88F440AA153DAC820097B4C3 /* ReactiveCocoa-iOS */,
+ 88CDF7DB15000FCF00163A9F /* ReactiveCocoaTests */,
+ 5FAF5222174D4C2000CAC810 /* ReactiveCocoaTests-iOS */,
+ 1860F411177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost */,
+ 1860F42F177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHostTests */,
+ D05AD39317F2D56F0080895B /* ReactiveCocoa-Mac */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 1860F410177C91B500C7B3C9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1860F41E177C91B500C7B3C9 /* InfoPlist.strings in Resources */,
+ 1860F426177C91B500C7B3C9 /* Default.png in Resources */,
+ 1860F428177C91B500C7B3C9 /* Default@2x.png in Resources */,
+ 1860F42A177C91B500C7B3C9 /* Default-568h@2x.png in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 1860F42D177C91B500C7B3C9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1860F43C177C91B500C7B3C9 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 5FAF5220174D4C2000CAC810 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D090768E17FBED2E00EB087A /* test-data.json in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88037F8115056328001A5B19 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88CDF7D915000FCF00163A9F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D090768D17FBED2E00EB087A /* test-data.json in Resources */,
+ 88CDF7E715000FCF00163A9F /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 1860F40E177C91B500C7B3C9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CD11C6FF18714F00007C7CFD /* RACTestTableViewController.m in Sources */,
+ 1860F420177C91B500C7B3C9 /* main.m in Sources */,
+ 1860F424177C91B500C7B3C9 /* RACAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 1860F42B177C91B500C7B3C9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CD11C700187151AE007C7CFD /* UITableViewHeaderFooterViewRACSupportSpec.m in Sources */,
+ 1668028317FE775200C724B4 /* UICollectionReusableViewRACSupportSpec.m in Sources */,
+ 5564537F18107203002BD2E4 /* RACControlCommandExamples.m in Sources */,
+ D0F117CC179F0B97006CE68F /* UITableViewCellRACSupportSpec.m in Sources */,
+ 1860F44F177C958300C7B3C9 /* UITextFieldRACSupportSpec.m in Sources */,
+ 1860F450177C958900C7B3C9 /* UITextViewRACSupportSpec.m in Sources */,
+ D004BC9C177E1A2B00A5B8C5 /* UIActionSheetRACSupportSpec.m in Sources */,
+ 5564542418107275002BD2E4 /* UIRefreshControlRACSupportSpec.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 5FAF521E174D4C2000CAC810 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5FAF523E174D4D3200CAC810 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */,
+ 5FAF523F174D4D3600CAC810 /* NSNotificationCenterRACSupportSpec.m in Sources */,
+ D090768B17FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m in Sources */,
+ 5FAF5240174D4D5600CAC810 /* NSObjectRACDeallocatingSpec.m in Sources */,
+ 5FAF5241174D4D5600CAC810 /* NSObjectRACLiftingSpec.m in Sources */,
+ 5FAF5242174D4D5600CAC810 /* NSObjectRACPropertySubscribingExamples.m in Sources */,
+ 5FAF5243174D4D5600CAC810 /* NSObjectRACPropertySubscribingSpec.m in Sources */,
+ 5FAF5244174D4D5600CAC810 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */,
+ 5FAF5246174D4D5600CAC810 /* RACBacktraceSpec.m in Sources */,
+ 5FAF5247174D4D5600CAC810 /* RACBlockTrampolineSpec.m in Sources */,
+ 5FAF5248174D4D5600CAC810 /* RACCommandSpec.m in Sources */,
+ 5FAF5249174D4D5600CAC810 /* RACCompoundDisposableSpec.m in Sources */,
+ BE527EA01863706B006349E8 /* NSUserDefaultsRACSupportSpec.m in Sources */,
+ 5FAF524A174D4D5600CAC810 /* RACEventSpec.m in Sources */,
+ 5FAF524B174D4D5600CAC810 /* RACKVOWrapperSpec.m in Sources */,
+ 5FAF524C174D4D5600CAC810 /* RACMulticastConnectionSpec.m in Sources */,
+ 5FAF524D174D4D5600CAC810 /* RACKVOChannelSpec.m in Sources */,
+ 5FAF524E174D4D5600CAC810 /* RACPropertySignalExamples.m in Sources */,
+ 5FAF524F174D4D5600CAC810 /* RACChannelExamples.m in Sources */,
+ 5FAF5250174D4D5600CAC810 /* RACChannelSpec.m in Sources */,
+ 5FAF5251174D4D5600CAC810 /* RACSchedulerSpec.m in Sources */,
+ 5FAF5252174D4D5600CAC810 /* RACSequenceAdditionsSpec.m in Sources */,
+ 5FAF5253174D4D5600CAC810 /* RACSequenceExamples.m in Sources */,
+ 5FAF5254174D4D5600CAC810 /* RACSequenceSpec.m in Sources */,
+ 5FAF5255174D4D5600CAC810 /* RACSignalSpec.m in Sources */,
+ 5FAF5256174D4D5600CAC810 /* RACStreamExamples.m in Sources */,
+ 5FAF5257174D4D5600CAC810 /* RACSubjectSpec.m in Sources */,
+ 5FAF5258174D4D5600CAC810 /* RACSubscriberExamples.m in Sources */,
+ 5FAF5259174D4D5600CAC810 /* RACSubscriberSpec.m in Sources */,
+ AC65FD52176DECB1005ED22B /* UIAlertViewRACSupportSpec.m in Sources */,
+ 5FAF525A174D4D5600CAC810 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */,
+ 5FAF525B174D4D5600CAC810 /* RACTestObject.m in Sources */,
+ 5FAF525C174D4D5600CAC810 /* RACTupleSpec.m in Sources */,
+ 5FAF525D174D4D5600CAC810 /* NSObjectRACSelectorSignalSpec.m in Sources */,
+ 5FAF525E174D4D5600CAC810 /* RACSubclassObject.m in Sources */,
+ 5FAF5262174D4D8F00CAC810 /* UIBarButtonItemRACSupportSpec.m in Sources */,
+ 88302BFE1762A9E6003633BD /* RACTestExampleScheduler.m in Sources */,
+ 8884DD6B1756B65300F6C379 /* RACSignalStartExamples.m in Sources */,
+ D028DB7E179E591E00D1042F /* RACSerialDisposableSpec.m in Sources */,
+ 5EE9A79B1760D88500EAF5A2 /* UIButtonRACSupportSpec.m in Sources */,
+ 88302C2F1762C180003633BD /* RACTargetQueueSchedulerSpec.m in Sources */,
+ D0A0E227176A84DB007273ED /* RACDisposableSpec.m in Sources */,
+ D066C796176D262500C242D2 /* UIControlRACSupportSpec.m in Sources */,
+ D066C79D176D263D00C242D2 /* RACTestUIButton.m in Sources */,
+ D0C55CE217759559008CDDCA /* RACDelegateProxySpec.m in Sources */,
+ D07200261788C57200987F70 /* RACTestSchedulerSpec.m in Sources */,
+ D075A72B17BCB7E100C24FB7 /* RACControlCommandExamples.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88037F7E15056328001A5B19 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 88A0B6D2165B2B09005DE8F3 /* RACBlockTrampoline.m in Sources */,
+ 88037FC71505647E001A5B19 /* NSObject+RACKVOWrapper.m in Sources */,
+ 88037FC91505648C001A5B19 /* RACSubscriber.m in Sources */,
+ 88037FCC1505648C001A5B19 /* RACCommand.m in Sources */,
+ 88037FCD1505648C001A5B19 /* NSControl+RACCommandSupport.m in Sources */,
+ D090768217FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */,
+ 88DA309815071CBA00C19D0F /* RACValueTransformer.m in Sources */,
+ 880B9177150B09190008488E /* RACSubject.m in Sources */,
+ 88D4AB3F1510F6C30011494F /* RACReplaySubject.m in Sources */,
+ 88977C3E1512914A00A09EC5 /* RACSignal.m in Sources */,
+ 883A84DB1513964B006DB4C7 /* RACBehaviorSubject.m in Sources */,
+ 883A84E01513B5EC006DB4C7 /* RACDisposable.m in Sources */,
+ 886678711518DCD800DE77EC /* NSObject+RACPropertySubscribing.m in Sources */,
+ 881B37CD152260BF0079220B /* RACUnit.m in Sources */,
+ 884476E5152367D100958F44 /* RACScopedDisposable.m in Sources */,
+ 88B76F8F153726B00053EAE2 /* RACTuple.m in Sources */,
+ 88E2C6B5153C771C00C7493C /* RACScheduler.m in Sources */,
+ 88F440D4153DADEA0097B4C3 /* NSObject+RACAppKitBindings.m in Sources */,
+ 886F702B1551CF920045D68B /* RACGroupedSignal.m in Sources */,
+ A1FCC374156754A7008C9686 /* RACObjCRuntime.m in Sources */,
+ BE527E9618636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */,
+ D0DFBCCE15DD6D40009DADB3 /* RACBacktrace.m in Sources */,
+ D0D910D015F915BD00AD2DDA /* RACSignal+Operations.m in Sources */,
+ 88FC735716114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m in Sources */,
+ 886CEAE4163DE942007632D1 /* NSObject+RACLifting.m in Sources */,
+ D013A3E11807B7450072B6CE /* RACEmptySignal.m in Sources */,
+ 887ACDA9165878A8009190AD /* NSInvocation+RACTypeParsing.m in Sources */,
+ D0E9676D1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m in Sources */,
+ D0E967711641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m in Sources */,
+ D0E967751641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m in Sources */,
+ D0E967791641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m in Sources */,
+ D0E9677D1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m in Sources */,
+ D0E967811641EF9C00FCFF06 /* RACArraySequence.m in Sources */,
+ D0E967851641EF9C00FCFF06 /* RACDynamicSequence.m in Sources */,
+ D0E967891641EF9C00FCFF06 /* RACEmptySequence.m in Sources */,
+ D03525D417E2EBC90099CBAB /* RACSignalProvider.d in Sources */,
+ D028DB76179E53CB00D1042F /* RACSerialDisposable.m in Sources */,
+ D0E9678D1641EF9C00FCFF06 /* RACSequence.m in Sources */,
+ D0E967911641EF9C00FCFF06 /* RACStringSequence.m in Sources */,
+ D0D487031642550100DD7605 /* RACStream.m in Sources */,
+ D0EE284D164D906B006954A4 /* RACSignalSequence.m in Sources */,
+ 881E86A41669304800667F7B /* RACCompoundDisposable.m in Sources */,
+ 881E87AE16695C5600667F7B /* RACQueueScheduler.m in Sources */,
+ 881E87B416695EDF00667F7B /* RACImmediateScheduler.m in Sources */,
+ 881E87C61669636000667F7B /* RACSubscriptionScheduler.m in Sources */,
+ 5F45A887168CFA3E00B58A2B /* RACKVOChannel.m in Sources */,
+ 5F6FE8551692568A00A8D7A6 /* RACChannel.m in Sources */,
+ 88C5A026169246140045EF05 /* RACMulticastConnection.m in Sources */,
+ 5F9743F91694A2460024EB82 /* RACEagerSequence.m in Sources */,
+ 5F773DEC169B46670023069D /* NSEnumerator+RACSequenceAdditions.m in Sources */,
+ D077A16F169B740200057BB1 /* RACEvent.m in Sources */,
+ 8837EA1816A5A33300FC3CDF /* RACKVOTrampoline.m in Sources */,
+ D0A0B01616EAA3D100C47593 /* NSText+RACSignalSupport.m in Sources */,
+ D020F3DB17F6A3E40092BED2 /* RACCompoundDisposableProvider.d in Sources */,
+ D0A0B03C16EAA9AC00C47593 /* NSControl+RACTextSignalSupport.m in Sources */,
+ D013A3E91807B7C30072B6CE /* RACReturnSignal.m in Sources */,
+ 6E58405616F22D7500F588A6 /* NSObject+RACDeallocating.m in Sources */,
+ 880D7A5C16F7B351004A3361 /* NSObject+RACSelectorSignal.m in Sources */,
+ D0307EDF1731AAE100D83211 /* RACTupleSequence.m in Sources */,
+ D07CD7181731BA3900DE2394 /* RACUnarySequence.m in Sources */,
+ 5FDC35051736F54700792E52 /* NSString+RACKeyPathUtilities.m in Sources */,
+ D0D243BD1741FA13004359C6 /* NSObject+RACDescription.m in Sources */,
+ 882D071A17614FA7009EDA69 /* RACTargetQueueScheduler.m in Sources */,
+ 74F1731B186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */,
+ D0A0E230176A8CD6007273ED /* RACPassthroughSubscriber.m in Sources */,
+ D013A3D91807B5ED0072B6CE /* RACErrorSignal.m in Sources */,
+ 7479F6E8186177D200575CDB /* RACIndexSetSequence.m in Sources */,
+ D05F9D3717984EC000FD7982 /* EXTRuntimeExtensions.m in Sources */,
+ D009307B1788AB7B00EE7E8B /* RACTestScheduler.m in Sources */,
+ 55C39DE417F1EC6E006DC60C /* NSData+RACSupport.m in Sources */,
+ 55C39DE517F1EC6E006DC60C /* NSFileHandle+RACSupport.m in Sources */,
+ 55C39DE617F1EC6E006DC60C /* NSNotificationCenter+RACSupport.m in Sources */,
+ D013A3F21807B9690072B6CE /* RACDynamicSignal.m in Sources */,
+ 55C39DE717F1EC6E006DC60C /* NSString+RACSupport.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88CDF7D715000FCF00163A9F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8820937C1501C8A600796685 /* RACSignalSpec.m in Sources */,
+ 889D0A8015974B2A00F833E3 /* RACSubjectSpec.m in Sources */,
+ D041376915D2281C004BBF80 /* RACKVOWrapperSpec.m in Sources */,
+ D028DB7D179E591E00D1042F /* RACSerialDisposableSpec.m in Sources */,
+ 882CCA1E15F1564D00937D6E /* RACCommandSpec.m in Sources */,
+ 884848B615F658B800B11BD0 /* NSControlRACSupportSpec.m in Sources */,
+ 88442A341608A9AD00636B49 /* RACTestObject.m in Sources */,
+ 88FC735B16114FFB00F8A774 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */,
+ 8851A38B16161D500050D47F /* NSObjectRACPropertySubscribingSpec.m in Sources */,
+ D0D487061642651400DD7605 /* RACSequenceSpec.m in Sources */,
+ D0487AB3164314430085D890 /* RACStreamExamples.m in Sources */,
+ D0C70F90164337A2007027B4 /* RACSequenceAdditionsSpec.m in Sources */,
+ D0C70F93164337E3007027B4 /* RACSequenceExamples.m in Sources */,
+ 886CEACD163DE669007632D1 /* RACBlockTrampolineSpec.m in Sources */,
+ 8801E7511644BDE200A155FE /* NSObjectRACLiftingSpec.m in Sources */,
+ D0C70EC616659333005AAD03 /* RACSubscriberExamples.m in Sources */,
+ D090768A17FBECBF00EB087A /* NSURLConnectionRACSupportSpec.m in Sources */,
+ D0C70EC8166595AD005AAD03 /* RACSubscriberSpec.m in Sources */,
+ 8803C011166732BA00C36839 /* RACSchedulerSpec.m in Sources */,
+ 881E86BA1669350B00667F7B /* RACCompoundDisposableSpec.m in Sources */,
+ D0700F4C1672994D00D7CD30 /* NSNotificationCenterRACSupportSpec.m in Sources */,
+ 5F2447AD167E87C50062180C /* RACKVOChannelSpec.m in Sources */,
+ D02221621678910900DBD031 /* RACTupleSpec.m in Sources */,
+ 5F7EFECF168FBC4B0037E500 /* RACChannelExamples.m in Sources */,
+ 5F7EFED0168FBC4B0037E500 /* RACChannelSpec.m in Sources */,
+ D0870C6F16884A0600D0E11D /* RACBacktraceSpec.m in Sources */,
+ 88C5A02916924BFC0045EF05 /* RACMulticastConnectionSpec.m in Sources */,
+ D0EDE76716968AB10072A780 /* RACPropertySignalExamples.m in Sources */,
+ BE527E9F1863705B006349E8 /* NSUserDefaultsRACSupportSpec.m in Sources */,
+ 5F773DF0169B48830023069D /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */,
+ D077A172169B79A900057BB1 /* RACEventSpec.m in Sources */,
+ D0A0B01816EAA5CC00C47593 /* NSTextRACSupportSpec.m in Sources */,
+ 6E58405F16F3414200F588A6 /* NSObjectRACDeallocatingSpec.m in Sources */,
+ 1E893381171647A5009071B0 /* NSObjectRACPropertySubscribingExamples.m in Sources */,
+ 880D7A6616F7BB1A004A3361 /* NSObjectRACSelectorSignalSpec.m in Sources */,
+ 880D7A6916F7BCC7004A3361 /* RACSubclassObject.m in Sources */,
+ 5FDC350F1736F81900792E52 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */,
+ D075A72A17BCB7E100C24FB7 /* RACControlCommandExamples.m in Sources */,
+ 88302BFD1762A9E6003633BD /* RACTestExampleScheduler.m in Sources */,
+ 4925E806181BCC71000B2FEE /* NSControllerRACSupportSpec.m in Sources */,
+ 8884DD651756ACF600F6C379 /* RACSignalStartExamples.m in Sources */,
+ 88302C2E1762C180003633BD /* RACTargetQueueSchedulerSpec.m in Sources */,
+ D0A0E226176A84DB007273ED /* RACDisposableSpec.m in Sources */,
+ D011F9D01782AFD400EE7E38 /* NSObjectRACAppKitBindingsSpec.m in Sources */,
+ D07200251788C57200987F70 /* RACTestSchedulerSpec.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 88F440A7153DAC820097B4C3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 27A887D11703DC6800040001 /* UIBarButtonItem+RACCommandSupport.m in Sources */,
+ 8882D4601673B0450080E7CD /* RACBlockTrampoline.m in Sources */,
+ 88F440BA153DAD570097B4C3 /* RACCommand.m in Sources */,
+ 88F440BC153DAD5A0097B4C3 /* RACSubscriber.m in Sources */,
+ 88F440BD153DAD5C0097B4C3 /* RACSignal.m in Sources */,
+ 88F440CF153DAD850097B4C3 /* NSObject+RACPropertySubscribing.m in Sources */,
+ ACB0EAF31797DDD400942FFC /* UIAlertView+RACSignalSupport.m in Sources */,
+ 88F440CE153DAD830097B4C3 /* NSObject+RACKVOWrapper.m in Sources */,
+ 88F440C1153DAD640097B4C3 /* RACReplaySubject.m in Sources */,
+ 88F440C0153DAD630097B4C3 /* RACSubject.m in Sources */,
+ 88F440C6153DAD6E0097B4C3 /* RACScopedDisposable.m in Sources */,
+ 88F440C3153DAD690097B4C3 /* RACBehaviorSubject.m in Sources */,
+ 88F440C9153DAD740097B4C3 /* RACUnit.m in Sources */,
+ 88F440CB153DAD780097B4C3 /* RACScheduler.m in Sources */,
+ 88F440CA153DAD760097B4C3 /* RACTuple.m in Sources */,
+ 88F440C5153DAD6C0097B4C3 /* RACDisposable.m in Sources */,
+ D090768317FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */,
+ 88F44263153DC2C70097B4C3 /* UIControl+RACSignalSupport.m in Sources */,
+ 886F702C1551CF9D0045D68B /* RACGroupedSignal.m in Sources */,
+ 88F44267153DCAC50097B4C3 /* UITextField+RACSignalSupport.m in Sources */,
+ A1FCC27715666AA3008C9686 /* UITextView+RACSignalSupport.m in Sources */,
+ D03525D917E2FAAD0099CBAB /* RACSignalProvider.d in Sources */,
+ A1FCC375156754A7008C9686 /* RACObjCRuntime.m in Sources */,
+ A1FCC37B1567DED0008C9686 /* RACDelegateProxy.m in Sources */,
+ D0DFBCCF15DD6D40009DADB3 /* RACBacktrace.m in Sources */,
+ D0D910D115F915BD00AD2DDA /* RACSignal+Operations.m in Sources */,
+ 88FC735816114F9C00F8A774 /* RACSubscriptingAssignmentTrampoline.m in Sources */,
+ CD11C6F418714CD0007C7CFD /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */,
+ 886CEAE5163DE942007632D1 /* NSObject+RACLifting.m in Sources */,
+ 887ACDAA165878A8009190AD /* NSInvocation+RACTypeParsing.m in Sources */,
+ D0E9676E1641EF9C00FCFF06 /* NSArray+RACSequenceAdditions.m in Sources */,
+ D013A3E21807B7450072B6CE /* RACEmptySignal.m in Sources */,
+ D0E967721641EF9C00FCFF06 /* NSDictionary+RACSequenceAdditions.m in Sources */,
+ D0E967761641EF9C00FCFF06 /* NSOrderedSet+RACSequenceAdditions.m in Sources */,
+ D0E9677A1641EF9C00FCFF06 /* NSSet+RACSequenceAdditions.m in Sources */,
+ D0E9677E1641EF9C00FCFF06 /* NSString+RACSequenceAdditions.m in Sources */,
+ D0E967821641EF9C00FCFF06 /* RACArraySequence.m in Sources */,
+ D0E967861641EF9C00FCFF06 /* RACDynamicSequence.m in Sources */,
+ D028DB88179E616700D1042F /* UITableViewCell+RACSignalSupport.m in Sources */,
+ BE527E9718636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */,
+ D0E9678A1641EF9C00FCFF06 /* RACEmptySequence.m in Sources */,
+ D0E9678E1641EF9C00FCFF06 /* RACSequence.m in Sources */,
+ D0E967921641EF9C00FCFF06 /* RACStringSequence.m in Sources */,
+ D013A3F31807B9690072B6CE /* RACDynamicSignal.m in Sources */,
+ D0D487041642550100DD7605 /* RACStream.m in Sources */,
+ D013A3DA1807B5ED0072B6CE /* RACErrorSignal.m in Sources */,
+ D0EE284E164D906B006954A4 /* RACSignalSequence.m in Sources */,
+ 74F1731C186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */,
+ 881E86A51669304800667F7B /* RACCompoundDisposable.m in Sources */,
+ 881E87AF16695C5600667F7B /* RACQueueScheduler.m in Sources */,
+ 881E87B516695EDF00667F7B /* RACImmediateScheduler.m in Sources */,
+ 1668028017FE75F900C724B4 /* UICollectionReusableView+RACSignalSupport.m in Sources */,
+ 881E87C71669636000667F7B /* RACSubscriptionScheduler.m in Sources */,
+ 5F45A888168CFA3E00B58A2B /* RACKVOChannel.m in Sources */,
+ D028DB77179E53CB00D1042F /* RACSerialDisposable.m in Sources */,
+ 5F6FE8561692568A00A8D7A6 /* RACChannel.m in Sources */,
+ 88C5A027169246140045EF05 /* RACMulticastConnection.m in Sources */,
+ 5F9743FA1694A2460024EB82 /* RACEagerSequence.m in Sources */,
+ 5F773DED169B46670023069D /* NSEnumerator+RACSequenceAdditions.m in Sources */,
+ D077A170169B740200057BB1 /* RACEvent.m in Sources */,
+ 8837EA1916A5A33300FC3CDF /* RACKVOTrampoline.m in Sources */,
+ 6E58405D16F22F7800F588A6 /* NSObject+RACDeallocating.m in Sources */,
+ 880D7A5D16F7B351004A3361 /* NSObject+RACSelectorSignal.m in Sources */,
+ D0307EE01731AAE100D83211 /* RACTupleSequence.m in Sources */,
+ D07CD7191731BA3900DE2394 /* RACUnarySequence.m in Sources */,
+ 554D9E5E181064E200F21262 /* UIRefreshControl+RACCommandSupport.m in Sources */,
+ 1EC06B18173CB04000365258 /* UIGestureRecognizer+RACSignalSupport.m in Sources */,
+ 5FDC35061736F54700792E52 /* NSString+RACKeyPathUtilities.m in Sources */,
+ D0D243BE1741FA13004359C6 /* NSObject+RACDescription.m in Sources */,
+ 5EE9A7941760D61300EAF5A2 /* UIButton+RACCommandSupport.m in Sources */,
+ 882D071F17615139009EDA69 /* RACTargetQueueScheduler.m in Sources */,
+ D0A0E231176A8CD6007273ED /* RACPassthroughSubscriber.m in Sources */,
+ 557A4B5B177648C7008EF796 /* UIActionSheet+RACSignalSupport.m in Sources */,
+ D05F9D3817984EC000FD7982 /* EXTRuntimeExtensions.m in Sources */,
+ D009307C1788AB7B00EE7E8B /* RACTestScheduler.m in Sources */,
+ 5F70B2B017AB1829009AEDF9 /* UIDatePicker+RACSignalSupport.m in Sources */,
+ 5F70B2BF17AB1857009AEDF9 /* UISegmentedControl+RACSignalSupport.m in Sources */,
+ 5F70B2C117AB1857009AEDF9 /* UISlider+RACSignalSupport.m in Sources */,
+ 5F70B2C317AB1857009AEDF9 /* UIStepper+RACSignalSupport.m in Sources */,
+ 5F70B2C517AB1857009AEDF9 /* UISwitch+RACSignalSupport.m in Sources */,
+ 5F016DF717B10AA8002EEC69 /* UIControl+RACSignalSupportPrivate.m in Sources */,
+ D013A3EA1807B7C30072B6CE /* RACReturnSignal.m in Sources */,
+ D020F3DC17F6A3E40092BED2 /* RACCompoundDisposableProvider.d in Sources */,
+ 7479F6E9186177D200575CDB /* RACIndexSetSequence.m in Sources */,
+ 55C39DE817F1EC6E006DC60C /* NSData+RACSupport.m in Sources */,
+ 55C39DE917F1EC6E006DC60C /* NSFileHandle+RACSupport.m in Sources */,
+ 55C39DEA17F1EC6E006DC60C /* NSNotificationCenter+RACSupport.m in Sources */,
+ 55C39DEB17F1EC6E006DC60C /* NSString+RACSupport.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D05AD39017F2D56F0080895B /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D05AD3CF17F2DB100080895B /* NSInvocation+RACTypeParsing.m in Sources */,
+ D05AD3F117F2DB4F0080895B /* RACDynamicSequence.m in Sources */,
+ D05AD3F517F2DB4F0080895B /* RACSignalSequence.m in Sources */,
+ D05AD3C217F2DB100080895B /* RACBlockTrampoline.m in Sources */,
+ D05AD41917F2DB6A0080895B /* NSSet+RACSequenceAdditions.m in Sources */,
+ D090768417FBEADE00EB087A /* NSURLConnection+RACSupport.m in Sources */,
+ D05AD3CE17F2DB100080895B /* NSObject+RACLifting.m in Sources */,
+ D05AD3FE17F2DB5D0080895B /* RACQueueScheduler.m in Sources */,
+ D05AD42517F2DB6E0080895B /* NSText+RACSignalSupport.m in Sources */,
+ D05AD43017F2DB840080895B /* RACValueTransformer.m in Sources */,
+ D070CBC517FB5E370017F121 /* RACCompoundDisposableProvider.d in Sources */,
+ D05AD3F217F2DB4F0080895B /* RACEagerSequence.m in Sources */,
+ D05AD3F617F2DB4F0080895B /* RACTupleSequence.m in Sources */,
+ D05AD3E917F2DB230080895B /* RACSerialDisposable.m in Sources */,
+ D05AD42117F2DB6E0080895B /* NSControl+RACTextSignalSupport.m in Sources */,
+ D05AD40E17F2DB6A0080895B /* NSEnumerator+RACSequenceAdditions.m in Sources */,
+ D05AD41B17F2DB6A0080895B /* NSString+RACSequenceAdditions.m in Sources */,
+ D05AD43217F2DBCA0080895B /* RACSignalProvider.d in Sources */,
+ D05AD42D17F2DB840080895B /* RACKVOTrampoline.m in Sources */,
+ D05AD3D517F2DB1D0080895B /* RACSignal+Operations.m in Sources */,
+ BE527E9818636F7F006349E8 /* NSUserDefaults+RACSupport.m in Sources */,
+ D05AD41317F2DB6A0080895B /* NSObject+RACDescription.m in Sources */,
+ D05AD3CA17F2DB100080895B /* RACSubscriptingAssignmentTrampoline.m in Sources */,
+ D05AD3E717F2DB230080895B /* RACCompoundDisposable.m in Sources */,
+ D05AD3DD17F2DB1D0080895B /* RACSubject.m in Sources */,
+ D013A3E31807B7450072B6CE /* RACEmptySignal.m in Sources */,
+ D05AD3F417F2DB4F0080895B /* RACStringSequence.m in Sources */,
+ D05AD40C17F2DB6A0080895B /* NSDictionary+RACSequenceAdditions.m in Sources */,
+ D05AD41F17F2DB6E0080895B /* NSControl+RACCommandSupport.m in Sources */,
+ D05AD41517F2DB6A0080895B /* NSObject+RACSelectorSignal.m in Sources */,
+ D05AD3C817F2DB100080895B /* RACBacktrace.m in Sources */,
+ D05AD41217F2DB6A0080895B /* NSNotificationCenter+RACSupport.m in Sources */,
+ D05AD3D917F2DB1D0080895B /* RACMulticastConnection.m in Sources */,
+ D05AD43117F2DB950080895B /* RACObjCRuntime.m in Sources */,
+ D05AD3DB17F2DB1D0080895B /* RACGroupedSignal.m in Sources */,
+ D05AD3F017F2DB4F0080895B /* RACArraySequence.m in Sources */,
+ D05AD3E517F2DB230080895B /* RACScopedDisposable.m in Sources */,
+ D05AD40417F2DB5D0080895B /* RACSubscriptionScheduler.m in Sources */,
+ D05AD42917F2DB840080895B /* RACKVOChannel.m in Sources */,
+ D05AD42C17F2DB840080895B /* NSString+RACKeyPathUtilities.m in Sources */,
+ D05AD42717F2DB840080895B /* RACChannel.m in Sources */,
+ D05AD3FB17F2DB5D0080895B /* RACScheduler.m in Sources */,
+ D05AD3EB17F2DB270080895B /* RACCommand.m in Sources */,
+ D05AD3D117F2DB100080895B /* RACStream.m in Sources */,
+ D05AD3F717F2DB4F0080895B /* RACUnarySequence.m in Sources */,
+ D05AD3D317F2DB1D0080895B /* RACSignal.m in Sources */,
+ D05AD42F17F2DB840080895B /* NSObject+RACPropertySubscribing.m in Sources */,
+ D05AD3F317F2DB4F0080895B /* RACEmptySequence.m in Sources */,
+ D05AD42317F2DB6E0080895B /* NSObject+RACAppKitBindings.m in Sources */,
+ D05AD40817F2DB6A0080895B /* NSArray+RACSequenceAdditions.m in Sources */,
+ D05AD40017F2DB5D0080895B /* RACTargetQueueScheduler.m in Sources */,
+ D05AD41717F2DB6A0080895B /* NSOrderedSet+RACSequenceAdditions.m in Sources */,
+ D05AD40617F2DB5D0080895B /* RACTestScheduler.m in Sources */,
+ D05AD3EF17F2DB4F0080895B /* RACSequence.m in Sources */,
+ D05AD3BF17F2DA2A0080895B /* RACSubscriber.m in Sources */,
+ D013A3EB1807B7C30072B6CE /* RACReturnSignal.m in Sources */,
+ D05AD3DF17F2DB1D0080895B /* RACReplaySubject.m in Sources */,
+ D05AD3C417F2DB100080895B /* RACUnit.m in Sources */,
+ D05AD41017F2DB6A0080895B /* NSFileHandle+RACSupport.m in Sources */,
+ D05AD41D17F2DB6A0080895B /* NSString+RACSupport.m in Sources */,
+ D05AD3E317F2DB230080895B /* RACDisposable.m in Sources */,
+ D05AD40217F2DB5D0080895B /* RACImmediateScheduler.m in Sources */,
+ D05AD3CC17F2DB100080895B /* NSObject+RACDeallocating.m in Sources */,
+ 74F1731D186024A900BC937C /* NSIndexSet+RACSequenceAdditions.m in Sources */,
+ D05AD40A17F2DB6A0080895B /* NSData+RACSupport.m in Sources */,
+ D013A3DB1807B5ED0072B6CE /* RACErrorSignal.m in Sources */,
+ 7479F6EA186177D200575CDB /* RACIndexSetSequence.m in Sources */,
+ D05AD42B17F2DB840080895B /* NSObject+RACKVOWrapper.m in Sources */,
+ D05AD3E117F2DB1D0080895B /* RACBehaviorSubject.m in Sources */,
+ D05AD3D717F2DB1D0080895B /* RACEvent.m in Sources */,
+ D05AD3C117F2DB100080895B /* RACPassthroughSubscriber.m in Sources */,
+ D05AD3C617F2DB100080895B /* RACTuple.m in Sources */,
+ D013A3F41807B9690072B6CE /* RACDynamicSignal.m in Sources */,
+ D049804217F91F42001EE042 /* EXTRuntimeExtensions.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 1860F436177C91B500C7B3C9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 1860F411177C91B500C7B3C9 /* ReactiveCocoa-iOS-UIKitTestHost */;
+ targetProxy = 1860F435177C91B500C7B3C9 /* PBXContainerItemProxy */;
+ };
+ 88037FDB150564E9001A5B19 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 88037F8215056328001A5B19 /* ReactiveCocoa */;
+ targetProxy = 88037FDA150564E9001A5B19 /* PBXContainerItemProxy */;
+ };
+ D0ED9DB617501806003859A6 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 88F440AA153DAC820097B4C3 /* ReactiveCocoa-iOS */;
+ targetProxy = D0ED9DB517501806003859A6 /* PBXContainerItemProxy */;
+ };
+ D0F117C3179F0A91006CE68F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 88F440AA153DAC820097B4C3 /* ReactiveCocoa-iOS */;
+ targetProxy = D0F117C2179F0A91006CE68F /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 1860F41C177C91B500C7B3C9 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 1860F41D177C91B500C7B3C9 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 1860F43A177C91B500C7B3C9 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 1860F43B177C91B500C7B3C9 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 88CDF7C815000FCE00163A9F /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 88CDF7C915000FCE00163A9F /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 88CDF7E515000FCF00163A9F /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 88CDF7E615000FCF00163A9F /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 1860F440177C91B500C7B3C9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 1860F441177C91B500C7B3C9 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Test;
+ };
+ 1860F442177C91B500C7B3C9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ 1860F443177C91B500C7B3C9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Profile;
+ };
+ 1860F444177C91B500C7B3C9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoa-iOS-UIKitTestHost.app/ReactiveCocoa-iOS-UIKitTestHost";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"\n\n//:configuration",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist";
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ 1860F445177C91B500C7B3C9 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoa-iOS-UIKitTestHost.app/ReactiveCocoa-iOS-UIKitTestHost";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"\n\n//:configuration",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist";
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Test;
+ };
+ 1860F446177C91B500C7B3C9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoa-iOS-UIKitTestHost.app/ReactiveCocoa-iOS-UIKitTestHost";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"\n\n//:configuration",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+ 1860F447177C91B500C7B3C9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoa-iOS-UIKitTestHost.app/ReactiveCocoa-iOS-UIKitTestHost";
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"\n\n//:configuration",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist";
+ OTHER_LDFLAGS = "-all_load";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Profile;
+ };
+ 5FAF5233174D4C2000CAC810 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ 5FAF5234174D4C2000CAC810 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Test;
+ };
+ 5FAF5235174D4C2000CAC810 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+ 5FAF5236174D4C2000CAC810 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45D17775B1000906BF7 /* iOS-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "\"$(SDKROOT)/Developer/Library/Frameworks\"",
+ "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
+ );
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Profile;
+ };
+ 88037F9015056328001A5B19 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ INFOPLIST_FILE = "ReactiveCocoa/ReactiveCocoa-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+ 88037F9115056328001A5B19 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ INFOPLIST_FILE = "ReactiveCocoa/ReactiveCocoa-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+ 88997CAA1728912B00C569A6 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45817775B1000906BF7 /* Test.xcconfig */;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
+ GCC_OPTIMIZATION_LEVEL = fast;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_STRICT_SELECTOR_MATCH = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ SDKROOT = macosx;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = x86_64;
+ WARNING_CFLAGS = (
+ "-Werror",
+ "-Wall",
+ );
+ };
+ name = Test;
+ };
+ 88997CAB1728912B00C569A6 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ INFOPLIST_FILE = "ReactiveCocoa/ReactiveCocoa-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Test;
+ };
+ 88997CAC1728912B00C569A6 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = include/ReactiveCocoa;
+ };
+ name = Test;
+ };
+ 88997CAD1728912B00C569A6 /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46117775B1000906BF7 /* Mac-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = ReactiveCocoaTests;
+ VALID_ARCHS = x86_64;
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Test;
+ };
+ 88CDF7EB15000FCF00163A9F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45517775B1000906BF7 /* Debug.xcconfig */;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_STRICT_SELECTOR_MATCH = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ SDKROOT = macosx;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = x86_64;
+ WARNING_CFLAGS = (
+ "-Werror",
+ "-Wall",
+ );
+ };
+ name = Debug;
+ };
+ 88CDF7EC15000FCF00163A9F /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45717775B1000906BF7 /* Release.xcconfig */;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_STRICT_SELECTOR_MATCH = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ SDKROOT = macosx;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = x86_64;
+ WARNING_CFLAGS = (
+ "-Werror",
+ "-Wall",
+ );
+ };
+ name = Release;
+ };
+ 88CDF7F115000FCF00163A9F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46117775B1000906BF7 /* Mac-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = ReactiveCocoaTests;
+ VALID_ARCHS = x86_64;
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ 88CDF7F215000FCF00163A9F /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46117775B1000906BF7 /* Mac-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = ReactiveCocoaTests;
+ VALID_ARCHS = x86_64;
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+ 88F440B4153DAC820097B4C3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = include/ReactiveCocoa;
+ };
+ name = Debug;
+ };
+ 88F440B5153DAC820097B4C3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = include/ReactiveCocoa;
+ };
+ name = Release;
+ };
+ D05AD3B117F2D5710080895B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ D05AD3B217F2D5710080895B /* Test */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Test;
+ };
+ D05AD3B317F2D5710080895B /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ D05AD3B417F2D5710080895B /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46517775B1000906BF7 /* Mac-StaticLibrary.xcconfig */;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ D095BDD115CB2F9D00E9BB13 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45617775B1000906BF7 /* Profile.xcconfig */;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_STRICT_SELECTOR_MATCH = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.7;
+ SDKROOT = macosx;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = x86_64;
+ WARNING_CFLAGS = (
+ "-Werror",
+ "-Wall",
+ );
+ };
+ name = Profile;
+ };
+ D095BDD215CB2F9D00E9BB13 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46417775B1000906BF7 /* Mac-Framework.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ INFOPLIST_FILE = "ReactiveCocoa/ReactiveCocoa-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Profile;
+ };
+ D095BDD315CB2F9D00E9BB13 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E45F17775B1000906BF7 /* iOS-StaticLibrary.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = "ReactiveCocoa/ReactiveCocoa-Prefix.pch";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = include/ReactiveCocoa;
+ };
+ name = Profile;
+ };
+ D095BDD415CB2F9D00E9BB13 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D094E46117775B1000906BF7 /* Mac-Application.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_PREFIX_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "\"$(PROJECT_DIR)/../external/specta/src\"",
+ "\"$(PROJECT_DIR)/../external/expecta/src\"/**",
+ );
+ INFOPLIST_FILE = "ReactiveCocoaTests/ReactiveCocoaTests-Info.plist";
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = ReactiveCocoaTests;
+ VALID_ARCHS = x86_64;
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Profile;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1860F44C177C91B500C7B3C9 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS-UIKitTestHost" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1860F440177C91B500C7B3C9 /* Debug */,
+ 1860F441177C91B500C7B3C9 /* Test */,
+ 1860F442177C91B500C7B3C9 /* Release */,
+ 1860F443177C91B500C7B3C9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1860F44D177C91B500C7B3C9 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS-UIKitTestHostTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1860F444177C91B500C7B3C9 /* Debug */,
+ 1860F445177C91B500C7B3C9 /* Test */,
+ 1860F446177C91B500C7B3C9 /* Release */,
+ 1860F447177C91B500C7B3C9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 5FAF523D174D4C2000CAC810 /* Build configuration list for PBXNativeTarget "ReactiveCocoaTests-iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5FAF5233174D4C2000CAC810 /* Debug */,
+ 5FAF5234174D4C2000CAC810 /* Test */,
+ 5FAF5235174D4C2000CAC810 /* Release */,
+ 5FAF5236174D4C2000CAC810 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 88037F8F15056328001A5B19 /* Build configuration list for PBXNativeTarget "ReactiveCocoa" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 88037F9015056328001A5B19 /* Debug */,
+ 88997CAB1728912B00C569A6 /* Test */,
+ 88037F9115056328001A5B19 /* Release */,
+ D095BDD215CB2F9D00E9BB13 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 88CDF7B515000FCE00163A9F /* Build configuration list for PBXProject "ReactiveCocoa" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 88CDF7EB15000FCF00163A9F /* Debug */,
+ 88997CAA1728912B00C569A6 /* Test */,
+ 88CDF7EC15000FCF00163A9F /* Release */,
+ D095BDD115CB2F9D00E9BB13 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 88CDF7F015000FCF00163A9F /* Build configuration list for PBXNativeTarget "ReactiveCocoaTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 88CDF7F115000FCF00163A9F /* Debug */,
+ 88997CAD1728912B00C569A6 /* Test */,
+ 88CDF7F215000FCF00163A9F /* Release */,
+ D095BDD415CB2F9D00E9BB13 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 88F440B3153DAC820097B4C3 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 88F440B4153DAC820097B4C3 /* Debug */,
+ 88997CAC1728912B00C569A6 /* Test */,
+ 88F440B5153DAC820097B4C3 /* Release */,
+ D095BDD315CB2F9D00E9BB13 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D05AD3BD17F2D5710080895B /* Build configuration list for PBXNativeTarget "ReactiveCocoa-Mac" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D05AD3B117F2D5710080895B /* Debug */,
+ D05AD3B217F2D5710080895B /* Test */,
+ D05AD3B317F2D5710080895B /* Release */,
+ D05AD3B417F2D5710080895B /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 88CDF7B215000FCE00163A9F /* Project object */;
+}
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..a8ff253
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:ReactiveCocoa.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme
new file mode 100644
index 0000000..b7efc3f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0510"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D05AD39317F2D56F0080895B"
+ BuildableName = "libReactiveCocoa-Mac.a"
+ BlueprintName = "ReactiveCocoa-Mac"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D05AD3A117F2D5700080895B"
+ BuildableName = "ReactiveCocoa-MacTests.xctest"
+ BlueprintName = "ReactiveCocoa-MacTests"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS-UIKitTestHost.xcscheme b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS-UIKitTestHost.xcscheme
new file mode 100644
index 0000000..306864a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS-UIKitTestHost.xcscheme
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F411177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHost.app"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHost"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F42F177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHostTests.octest"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHostTests"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Test">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F42F177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHostTests.octest"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHostTests"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F411177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHost.app"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHost"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F411177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHost.app"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHost"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Profile"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "1860F411177C91B500C7B3C9"
+ BuildableName = "ReactiveCocoa-iOS-UIKitTestHost.app"
+ BlueprintName = "ReactiveCocoa-iOS-UIKitTestHost"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme
new file mode 100644
index 0000000..a31da25
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0510"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "88F440AA153DAC820097B4C3"
+ BuildableName = "libReactiveCocoa-iOS.a"
+ BlueprintName = "ReactiveCocoa-iOS"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "5FAF5222174D4C2000CAC810"
+ BuildableName = "ReactiveCocoaTests-iOS.octest"
+ BlueprintName = "ReactiveCocoaTests-iOS"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Test">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "5FAF5222174D4C2000CAC810"
+ BuildableName = "ReactiveCocoaTests-iOS.octest"
+ BlueprintName = "ReactiveCocoaTests-iOS"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Profile"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa.xcscheme b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa.xcscheme
new file mode 100644
index 0000000..12209cd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa.xcscheme
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0510"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "88037F8215056328001A5B19"
+ BuildableName = "ReactiveCocoa.framework"
+ BlueprintName = "ReactiveCocoa"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "88CDF7DB15000FCF00163A9F"
+ BuildableName = "ReactiveCocoaTests.octest"
+ BlueprintName = "ReactiveCocoaTests"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "NO"
+ buildConfiguration = "Test">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "88CDF7DB15000FCF00163A9F"
+ BuildableName = "ReactiveCocoaTests.octest"
+ BlueprintName = "ReactiveCocoaTests"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "88037F8215056328001A5B19"
+ BuildableName = "ReactiveCocoa.framework"
+ BlueprintName = "ReactiveCocoa"
+ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "DYLD_INSERT_LIBRARIES"
+ value = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoa.framework/ReactiveCocoa"
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Profile"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcworkspace/contents.xcworkspacedata b/ReactiveCocoaFramework/ReactiveCocoa.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..4fb1f75
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:ReactiveCocoa.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../external/expecta/Expecta.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../external/specta/Specta.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.h
new file mode 100644
index 0000000..d2d0b3f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.h
@@ -0,0 +1,20 @@
+//
+// NSArray+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSArray (RACSequenceAdditions)
+
+/// Creates and returns a sequence corresponding to the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.m
new file mode 100644
index 0000000..ca2b951
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.m
@@ -0,0 +1,18 @@
+//
+// NSArray+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSArray+RACSequenceAdditions.h"
+#import "RACArraySequence.h"
+
+@implementation NSArray (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ return [RACArraySequence sequenceWithArray:self offset:0];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.h
new file mode 100644
index 0000000..4e51577
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.h
@@ -0,0 +1,22 @@
+//
+// NSControl+RACCommandSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/3/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class RACCommand;
+
+@interface NSControl (RACCommandSupport)
+
+/// Sets the control's command. When the control is clicked, the command is
+/// executed with the sender of the event. The control's enabledness is bound
+/// to the command's `canExecute`.
+///
+/// Note: this will reset the control's target and action.
+@property (nonatomic, strong) RACCommand *rac_command;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.m
new file mode 100644
index 0000000..8778f69
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.m
@@ -0,0 +1,59 @@
+//
+// NSControl+RACCommandSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/3/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSControl+RACCommandSupport.h"
+#import "EXTScope.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCommand.h"
+#import "RACScopedDisposable.h"
+#import "RACSignal+Operations.h"
+#import <objc/runtime.h>
+
+static void *NSControlRACCommandKey = &NSControlRACCommandKey;
+static void *NSControlEnabledDisposableKey = &NSControlEnabledDisposableKey;
+
+@implementation NSControl (RACCommandSupport)
+
+- (RACCommand *)rac_command {
+ return objc_getAssociatedObject(self, NSControlRACCommandKey);
+}
+
+- (void)setRac_command:(RACCommand *)command {
+ objc_setAssociatedObject(self, NSControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Tear down any previous binding before setting up our new one, or else we
+ // might get assertion failures.
+ [objc_getAssociatedObject(self, NSControlEnabledDisposableKey) dispose];
+ objc_setAssociatedObject(self, NSControlEnabledDisposableKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ if (command == nil) {
+ self.enabled = YES;
+ return;
+ }
+
+ [self rac_hijackActionAndTargetIfNeeded];
+
+ RACScopedDisposable *disposable = [[command.enabled setKeyPath:@"enabled" onObject:self] asScopedDisposable];
+ objc_setAssociatedObject(self, NSControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (void)rac_hijackActionAndTargetIfNeeded {
+ SEL hijackSelector = @selector(rac_commandPerformAction:);
+ if (self.target == self && self.action == hijackSelector) return;
+
+ if (self.target != nil) NSLog(@"WARNING: NSControl.rac_command hijacks the control's existing target and action.");
+
+ self.target = self;
+ self.action = hijackSelector;
+}
+
+- (void)rac_commandPerformAction:(id)sender {
+ [self.rac_command execute:sender];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.h
new file mode 100644
index 0000000..3d3618d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.h
@@ -0,0 +1,24 @@
+//
+// NSControl+RACTextSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-03-08.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class RACSignal;
+
+@interface NSControl (RACTextSignalSupport)
+
+/// Observes a text-based control for changes.
+///
+/// Using this method on a control without editable text is considered undefined
+/// behavior.
+///
+/// Returns a signal which sends the current string value of the receiver, then
+/// the new value any time it changes.
+- (RACSignal *)rac_textSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.m
new file mode 100644
index 0000000..3541bc2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.m
@@ -0,0 +1,38 @@
+//
+// NSControl+RACTextSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-03-08.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSControl+RACTextSignalSupport.h"
+#import "EXTScope.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+#import "NSObject+RACDescription.h"
+
+@implementation NSControl (RACTextSignalSupport)
+
+- (RACSignal *)rac_textSignal {
+ @weakify(self);
+ return [[[[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ @strongify(self);
+ id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) {
+ [subscriber sendNext:note.object];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [NSNotificationCenter.defaultCenter removeObserver:observer];
+ }];
+ }]
+ map:^(NSControl *control) {
+ return [control.stringValue copy];
+ }]
+ startWith:[self.stringValue copy]]
+ setNameWithFormat:@"%@ -rac_textSignal", [self rac_description]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.h
new file mode 100644
index 0000000..42198a6
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.h
@@ -0,0 +1,22 @@
+//
+// NSData+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+@class RACScheduler;
+
+@interface NSData (RACSupport)
+
+// Read the data at the URL using -[NSData initWithContentsOfURL:options:error:].
+// Sends the data or the error.
+//
+// scheduler - cannot be nil.
++ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.m
new file mode 100644
index 0000000..227eb4d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.m
@@ -0,0 +1,35 @@
+//
+// NSData+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSData+RACSupport.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+
+@implementation NSData (RACSupport)
+
++ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler {
+ NSCParameterAssert(scheduler != nil);
+
+ RACReplaySubject *subject = [RACReplaySubject subject];
+ [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ options: %lu scheduler: %@", URL, (unsigned long)options, scheduler];
+
+ [scheduler schedule:^{
+ NSError *error = nil;
+ NSData *data = [[NSData alloc] initWithContentsOfURL:URL options:options error:&error];
+ if(data == nil) {
+ [subject sendError:error];
+ } else {
+ [subject sendNext:data];
+ [subject sendCompleted];
+ }
+ }];
+
+ return subject;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.h
new file mode 100644
index 0000000..4871fe7
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.h
@@ -0,0 +1,31 @@
+//
+// NSDictionary+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSDictionary (RACSequenceAdditions)
+
+/// Creates and returns a sequence of RACTuple key/value pairs. The key will be
+/// the first element in the tuple, and the value will be the second.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+/// Creates and returns a sequence corresponding to the keys in the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_keySequence;
+
+/// Creates and returns a sequence corresponding to the values in the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_valueSequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.m
new file mode 100644
index 0000000..192e6fd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.m
@@ -0,0 +1,34 @@
+//
+// NSDictionary+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSDictionary+RACSequenceAdditions.h"
+#import "NSArray+RACSequenceAdditions.h"
+#import "RACSequence.h"
+#import "RACTuple.h"
+
+@implementation NSDictionary (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ NSDictionary *immutableDict = [self copy];
+
+ // TODO: First class support for dictionary sequences.
+ return [immutableDict.allKeys.rac_sequence map:^(id key) {
+ id value = immutableDict[key];
+ return [RACTuple tupleWithObjects:key, value, nil];
+ }];
+}
+
+- (RACSequence *)rac_keySequence {
+ return self.allKeys.rac_sequence;
+}
+
+- (RACSequence *)rac_valueSequence {
+ return self.allValues.rac_sequence;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.h
new file mode 100644
index 0000000..1d8fc84
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.h
@@ -0,0 +1,20 @@
+//
+// NSEnumerator+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 07/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSEnumerator (RACSequenceAdditions)
+
+/// Creates and returns a sequence corresponding to the receiver.
+///
+/// The receiver is exhausted lazily as the sequence is enumerated.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.m
new file mode 100644
index 0000000..aa56eaa
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.m
@@ -0,0 +1,22 @@
+//
+// NSEnumerator+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 07/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSEnumerator+RACSequenceAdditions.h"
+#import "RACSequence.h"
+
+@implementation NSEnumerator (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ return [RACSequence sequenceWithHeadBlock:^{
+ return [self nextObject];
+ } tailBlock:^{
+ return self.rac_sequence;
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.h
new file mode 100644
index 0000000..985398d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.h
@@ -0,0 +1,19 @@
+//
+// NSFileHandle+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/10/12.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+@interface NSFileHandle (RACSupport)
+
+// Read any available data in the background and send it. Completes when data
+// length is <= 0.
+- (RACSignal *)rac_readInBackground;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.m
new file mode 100644
index 0000000..a258789
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.m
@@ -0,0 +1,39 @@
+//
+// NSFileHandle+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/10/12.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSFileHandle+RACSupport.h"
+#import "NSNotificationCenter+RACSupport.h"
+#import "RACReplaySubject.h"
+#import "RACDisposable.h"
+
+@implementation NSFileHandle (RACSupport)
+
+- (RACSignal *)rac_readInBackground {
+ RACReplaySubject *subject = [RACReplaySubject subject];
+ [subject setNameWithFormat:@"%@ -rac_readInBackground", self];
+
+ RACSignal *dataNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:NSFileHandleReadCompletionNotification object:self] map:^(NSNotification *note) {
+ return [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
+ }];
+
+ __block RACDisposable *subscription = [dataNotification subscribeNext:^(NSData *data) {
+ if(data.length > 0) {
+ [subject sendNext:data];
+ [self readInBackgroundAndNotify];
+ } else {
+ [subject sendCompleted];
+ [subscription dispose];
+ }
+ }];
+
+ [self readInBackgroundAndNotify];
+
+ return subject;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.h
new file mode 100644
index 0000000..b2066e4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.h
@@ -0,0 +1,20 @@
+//
+// NSIndexSet+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Sergey Gavrilyuk on 12/17/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "RACSequence.h"
+
+@interface NSIndexSet (RACSequenceAdditions)
+
+/// Creates and returns a sequence of indexes (as `NSNumber`s) corresponding to
+/// the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.m
new file mode 100644
index 0000000..b3557e6
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSIndexSet+RACSequenceAdditions.m
@@ -0,0 +1,18 @@
+//
+// NSIndexSet+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Sergey Gavrilyuk on 12/17/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSIndexSet+RACSequenceAdditions.h"
+#import "RACIndexSetSequence.h"
+
+@implementation NSIndexSet (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ return [RACIndexSetSequence sequenceWithIndexSet:self];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.h b/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.h
new file mode 100644
index 0000000..d1e72bf
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.h
@@ -0,0 +1,56 @@
+//
+// NSInvocation+RACTypeParsing.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACTuple;
+
+// A private category of methods to handle wrapping and unwrapping of values.
+@interface NSInvocation (RACTypeParsing)
+
+// Sets the argument for the invocation at the given index by unboxing the given
+// object based on the type signature of the argument.
+//
+// This does not support C arrays or unions.
+//
+// Note that calling this on a char * or const char * argument can cause all
+// arguments to be retained.
+//
+// object - The object to unbox and set as the argument.
+// index - The index of the argument to set.
+- (void)rac_setArgument:(id)object atIndex:(NSUInteger)index;
+
+// Gets the argument for the invocation at the given index based on the
+// invocation's method signature. The value is then wrapped in the appropriate
+// object type.
+//
+// This does not support C arrays or unions.
+//
+// index - The index of the argument to get.
+//
+// Returns the argument of the invocation, wrapped in an object.
+- (id)rac_argumentAtIndex:(NSUInteger)index;
+
+// Arguments tuple for the invocation.
+//
+// The arguments tuple excludes implicit variables `self` and `_cmd`.
+//
+// See -rac_argumentAtIndex: and -rac_setArgumentAtIndex: for further
+// description of the underlying behavior.
+@property (nonatomic, copy) RACTuple *rac_argumentsTuple;
+
+// Gets the return value from the invocation based on the invocation's method
+// signature. The value is then wrapped in the appropriate object type.
+//
+// This does not support C arrays or unions.
+//
+// Returns the return value of the invocation, wrapped in an object. Voids are
+// returned as `RACUnit.defaultUnit`.
+- (id)rac_returnValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.m b/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.m
new file mode 100644
index 0000000..a5318e7
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.m
@@ -0,0 +1,232 @@
+//
+// NSInvocation+RACTypeParsing.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSInvocation+RACTypeParsing.h"
+#import "RACTuple.h"
+#import "RACUnit.h"
+#import <CoreGraphics/CoreGraphics.h>
+
+@implementation NSInvocation (RACTypeParsing)
+
+- (void)rac_setArgument:(id)object atIndex:(NSUInteger)index {
+#define PULL_AND_SET(type, selector) \
+ do { \
+ type val = [object selector]; \
+ [self setArgument:&val atIndex:(NSInteger)index]; \
+ } while(0)
+
+ const char *argType = [self.methodSignature getArgumentTypeAtIndex:index];
+ // Skip const type qualifier.
+ if (argType[0] == 'r') {
+ argType++;
+ }
+
+ if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) {
+ [self setArgument:&object atIndex:(NSInteger)index];
+ } else if (strcmp(argType, @encode(char)) == 0) {
+ PULL_AND_SET(char, charValue);
+ } else if (strcmp(argType, @encode(int)) == 0) {
+ PULL_AND_SET(int, intValue);
+ } else if (strcmp(argType, @encode(short)) == 0) {
+ PULL_AND_SET(short, shortValue);
+ } else if (strcmp(argType, @encode(long)) == 0) {
+ PULL_AND_SET(long, longValue);
+ } else if (strcmp(argType, @encode(long long)) == 0) {
+ PULL_AND_SET(long long, longLongValue);
+ } else if (strcmp(argType, @encode(unsigned char)) == 0) {
+ PULL_AND_SET(unsigned char, unsignedCharValue);
+ } else if (strcmp(argType, @encode(unsigned int)) == 0) {
+ PULL_AND_SET(unsigned int, unsignedIntValue);
+ } else if (strcmp(argType, @encode(unsigned short)) == 0) {
+ PULL_AND_SET(unsigned short, unsignedShortValue);
+ } else if (strcmp(argType, @encode(unsigned long)) == 0) {
+ PULL_AND_SET(unsigned long, unsignedLongValue);
+ } else if (strcmp(argType, @encode(unsigned long long)) == 0) {
+ PULL_AND_SET(unsigned long long, unsignedLongLongValue);
+ } else if (strcmp(argType, @encode(float)) == 0) {
+ PULL_AND_SET(float, floatValue);
+ } else if (strcmp(argType, @encode(double)) == 0) {
+ PULL_AND_SET(double, doubleValue);
+ } else if (strcmp(argType, @encode(BOOL)) == 0) {
+ PULL_AND_SET(BOOL, boolValue);
+ } else if (strcmp(argType, @encode(char *)) == 0) {
+ const char *cString = [object UTF8String];
+ [self setArgument:&cString atIndex:(NSInteger)index];
+ [self retainArguments];
+ } else if (strcmp(argType, @encode(void (^)(void))) == 0) {
+ [self setArgument:&object atIndex:(NSInteger)index];
+ } else {
+ NSCParameterAssert([object isKindOfClass:NSValue.class]);
+
+ NSUInteger valueSize = 0;
+ NSGetSizeAndAlignment([object objCType], &valueSize, NULL);
+
+#if DEBUG
+ NSUInteger argSize = 0;
+ NSGetSizeAndAlignment(argType, &argSize, NULL);
+ NSCAssert(valueSize == argSize, @"Value size does not match argument size in -rac_setArgument: %@ atIndex: %lu", object, (unsigned long)index);
+#endif
+
+ unsigned char valueBytes[valueSize];
+ [object getValue:valueBytes];
+
+ [self setArgument:valueBytes atIndex:(NSInteger)index];
+ }
+
+#undef PULL_AND_SET
+}
+
+- (id)rac_argumentAtIndex:(NSUInteger)index {
+#define WRAP_AND_RETURN(type) \
+ do { \
+ type val = 0; \
+ [self getArgument:&val atIndex:(NSInteger)index]; \
+ return @(val); \
+ } while (0)
+
+ const char *argType = [self.methodSignature getArgumentTypeAtIndex:index];
+ // Skip const type qualifier.
+ if (argType[0] == 'r') {
+ argType++;
+ }
+
+ if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) {
+ __autoreleasing id returnObj;
+ [self getArgument:&returnObj atIndex:(NSInteger)index];
+ return returnObj;
+ } else if (strcmp(argType, @encode(char)) == 0) {
+ WRAP_AND_RETURN(char);
+ } else if (strcmp(argType, @encode(int)) == 0) {
+ WRAP_AND_RETURN(int);
+ } else if (strcmp(argType, @encode(short)) == 0) {
+ WRAP_AND_RETURN(short);
+ } else if (strcmp(argType, @encode(long)) == 0) {
+ WRAP_AND_RETURN(long);
+ } else if (strcmp(argType, @encode(long long)) == 0) {
+ WRAP_AND_RETURN(long long);
+ } else if (strcmp(argType, @encode(unsigned char)) == 0) {
+ WRAP_AND_RETURN(unsigned char);
+ } else if (strcmp(argType, @encode(unsigned int)) == 0) {
+ WRAP_AND_RETURN(unsigned int);
+ } else if (strcmp(argType, @encode(unsigned short)) == 0) {
+ WRAP_AND_RETURN(unsigned short);
+ } else if (strcmp(argType, @encode(unsigned long)) == 0) {
+ WRAP_AND_RETURN(unsigned long);
+ } else if (strcmp(argType, @encode(unsigned long long)) == 0) {
+ WRAP_AND_RETURN(unsigned long long);
+ } else if (strcmp(argType, @encode(float)) == 0) {
+ WRAP_AND_RETURN(float);
+ } else if (strcmp(argType, @encode(double)) == 0) {
+ WRAP_AND_RETURN(double);
+ } else if (strcmp(argType, @encode(BOOL)) == 0) {
+ WRAP_AND_RETURN(BOOL);
+ } else if (strcmp(argType, @encode(char *)) == 0) {
+ WRAP_AND_RETURN(const char *);
+ } else if (strcmp(argType, @encode(void (^)(void))) == 0) {
+ __unsafe_unretained id block = nil;
+ [self getArgument:&block atIndex:(NSInteger)index];
+ return [block copy];
+ } else {
+ NSUInteger valueSize = 0;
+ NSGetSizeAndAlignment(argType, &valueSize, NULL);
+
+ unsigned char valueBytes[valueSize];
+ [self getArgument:valueBytes atIndex:(NSInteger)index];
+
+ return [NSValue valueWithBytes:valueBytes objCType:argType];
+ }
+
+ return nil;
+
+#undef WRAP_AND_RETURN
+}
+
+- (RACTuple *)rac_argumentsTuple {
+ NSUInteger numberOfArguments = self.methodSignature.numberOfArguments;
+ NSMutableArray *argumentsArray = [NSMutableArray arrayWithCapacity:numberOfArguments - 2];
+ for (NSUInteger index = 2; index < numberOfArguments; index++) {
+ [argumentsArray addObject:[self rac_argumentAtIndex:index] ?: RACTupleNil.tupleNil];
+ }
+
+ return [RACTuple tupleWithObjectsFromArray:argumentsArray];
+}
+
+- (void)setRac_argumentsTuple:(RACTuple *)arguments {
+ NSCAssert(arguments.count == self.methodSignature.numberOfArguments - 2, @"Number of supplied arguments (%lu), does not match the number expected by the signature (%lu)", (unsigned long)arguments.count, (unsigned long)self.methodSignature.numberOfArguments - 2);
+
+ NSUInteger index = 2;
+ for (id arg in arguments) {
+ [self rac_setArgument:(arg == RACTupleNil.tupleNil ? nil : arg) atIndex:index];
+ index++;
+ }
+}
+
+- (id)rac_returnValue {
+#define WRAP_AND_RETURN(type) \
+ do { \
+ type val = 0; \
+ [self getReturnValue:&val]; \
+ return @(val); \
+ } while (0)
+
+ const char *returnType = self.methodSignature.methodReturnType;
+ // Skip const type qualifier.
+ if (returnType[0] == 'r') {
+ returnType++;
+ }
+
+ if (strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0 || strcmp(returnType, @encode(void (^)(void))) == 0) {
+ __autoreleasing id returnObj;
+ [self getReturnValue:&returnObj];
+ return returnObj;
+ } else if (strcmp(returnType, @encode(char)) == 0) {
+ WRAP_AND_RETURN(char);
+ } else if (strcmp(returnType, @encode(int)) == 0) {
+ WRAP_AND_RETURN(int);
+ } else if (strcmp(returnType, @encode(short)) == 0) {
+ WRAP_AND_RETURN(short);
+ } else if (strcmp(returnType, @encode(long)) == 0) {
+ WRAP_AND_RETURN(long);
+ } else if (strcmp(returnType, @encode(long long)) == 0) {
+ WRAP_AND_RETURN(long long);
+ } else if (strcmp(returnType, @encode(unsigned char)) == 0) {
+ WRAP_AND_RETURN(unsigned char);
+ } else if (strcmp(returnType, @encode(unsigned int)) == 0) {
+ WRAP_AND_RETURN(unsigned int);
+ } else if (strcmp(returnType, @encode(unsigned short)) == 0) {
+ WRAP_AND_RETURN(unsigned short);
+ } else if (strcmp(returnType, @encode(unsigned long)) == 0) {
+ WRAP_AND_RETURN(unsigned long);
+ } else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
+ WRAP_AND_RETURN(unsigned long long);
+ } else if (strcmp(returnType, @encode(float)) == 0) {
+ WRAP_AND_RETURN(float);
+ } else if (strcmp(returnType, @encode(double)) == 0) {
+ WRAP_AND_RETURN(double);
+ } else if (strcmp(returnType, @encode(BOOL)) == 0) {
+ WRAP_AND_RETURN(BOOL);
+ } else if (strcmp(returnType, @encode(char *)) == 0) {
+ WRAP_AND_RETURN(const char *);
+ } else if (strcmp(returnType, @encode(void)) == 0) {
+ return RACUnit.defaultUnit;
+ } else {
+ NSUInteger valueSize = 0;
+ NSGetSizeAndAlignment(returnType, &valueSize, NULL);
+
+ unsigned char valueBytes[valueSize];
+ [self getReturnValue:valueBytes];
+
+ return [NSValue valueWithBytes:valueBytes objCType:returnType];
+ }
+
+ return nil;
+
+#undef WRAP_AND_RETURN
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.h
new file mode 100644
index 0000000..ddb3954
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.h
@@ -0,0 +1,18 @@
+//
+// NSNotificationCenter+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/10/12.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+@interface NSNotificationCenter (RACSupport)
+
+// Sends the NSNotification every time the notification is posted.
+- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.m
new file mode 100644
index 0000000..a0112e9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.m
@@ -0,0 +1,31 @@
+//
+// NSNotificationCenter+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/10/12.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSNotificationCenter+RACSupport.h"
+#import "EXTScope.h"
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+#import "RACDisposable.h"
+
+@implementation NSNotificationCenter (RACSupport)
+
+- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object {
+ @unsafeify(object);
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ @strongify(object);
+ id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
+ [subscriber sendNext:note];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [self removeObserver:observer];
+ }];
+ }] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.h
new file mode 100644
index 0000000..eaec439
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.h
@@ -0,0 +1,40 @@
+//
+// NSObject+RACAppKitBindings.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class RACChannelTerminal;
+
+@interface NSObject (RACAppKitBindings)
+
+/// Invokes -rac_channelToBinding:options: without any options.
+- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding;
+
+/// Applies a Cocoa binding to the receiver, then exposes a RACChannel-based
+/// interface for manipulating it.
+///
+/// Creating two of the same bindings on the same object will result in undefined
+/// behavior.
+///
+/// binding - The name of the binding. This must not be nil.
+/// options - Any options to pass to Cocoa Bindings. This may be nil.
+///
+/// Returns a RACChannelTerminal which will send future values from the receiver,
+/// and update the receiver when values are sent to the terminal.
+- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding options:(NSDictionary *)options;
+
+@end
+
+@interface NSObject (RACAppKitBindingsDeprecated)
+
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath __attribute__((deprecated("Use -rac_bind:options: instead")));
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath nilValue:(id)nilValue __attribute__((deprecated("Use -rac_bind:options: instead")));
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath transform:(id (^)(id value))transformBlock __attribute__((deprecated("Use -rac_bind:options: instead")));
+- (void)rac_bind:(NSString *)binding toObject:(id)object withNegatedKeyPath:(NSString *)keyPath __attribute__((deprecated("Use -rac_bind:options: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.m
new file mode 100644
index 0000000..ea80f83
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.m
@@ -0,0 +1,175 @@
+//
+// NSObject+RACAppKitBindings.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACAppKitBindings.h"
+#import "EXTKeyPathCoding.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACChannel.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOChannel.h"
+#import "RACMulticastConnection.h"
+#import "RACSignal+Operations.h"
+#import "RACValueTransformer.h"
+#import <objc/runtime.h>
+
+// Used as an object to bind to, so we can hide the object creation and just
+// expose a RACChannel instead.
+@interface RACChannelProxy : NSObject
+
+// The RACChannel used for this Cocoa binding.
+@property (nonatomic, strong, readonly) RACChannel *channel;
+
+// The KVC- and KVO-compliant property to be read and written by the Cocoa
+// binding.
+//
+// This should not be set manually.
+@property (nonatomic, strong) id value;
+
+// The target of the Cocoa binding.
+//
+// This should be set to nil when the target deallocates.
+@property (atomic, unsafe_unretained) id target;
+
+// The name of the Cocoa binding used.
+@property (nonatomic, copy, readonly) NSString *bindingName;
+
+// Improves the performance of KVO on the receiver.
+//
+// See the documentation for <NSKeyValueObserving> for more information.
+@property (atomic, assign) void *observationInfo;
+
+// Initializes the receiver and binds to the given target.
+//
+// target - The target of the Cocoa binding. This must not be nil.
+// bindingName - The name of the Cocoa binding to use. This must not be nil.
+// options - Any options to pass to the binding. This may be nil.
+//
+// Returns an initialized channel proxy.
+- (id)initWithTarget:(id)target bindingName:(NSString *)bindingName options:(NSDictionary *)options;
+
+@end
+
+@implementation NSObject (RACAppKitBindings)
+
+- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding {
+ return [self rac_channelToBinding:binding options:nil];
+}
+
+- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding options:(NSDictionary *)options {
+ NSCParameterAssert(binding != nil);
+
+ RACChannelProxy *proxy = [[RACChannelProxy alloc] initWithTarget:self bindingName:binding options:options];
+ return proxy.channel.leadingTerminal;
+}
+
+@end
+
+@implementation RACChannelProxy
+
+#pragma mark Properties
+
+- (void)setValue:(id)value {
+ [self willChangeValueForKey:@keypath(self.value)];
+ _value = value;
+ [self didChangeValueForKey:@keypath(self.value)];
+}
+
+#pragma mark Lifecycle
+
+- (id)initWithTarget:(id)target bindingName:(NSString *)bindingName options:(NSDictionary *)options {
+ NSCParameterAssert(target != nil);
+ NSCParameterAssert(bindingName != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _target = target;
+ _bindingName = [bindingName copy];
+ _channel = [[RACChannel alloc] init];
+
+ @weakify(self);
+
+ void (^cleanUp)() = ^{
+ @strongify(self);
+
+ id target = self.target;
+ if (target == nil) return;
+
+ self.target = nil;
+
+ [target unbind:bindingName];
+ objc_setAssociatedObject(target, (__bridge void *)self, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ };
+
+ // When the channel terminates, tear down this proxy.
+ [self.channel.followingTerminal subscribeError:^(NSError *error) {
+ cleanUp();
+ } completed:cleanUp];
+
+ [self.target bind:bindingName toObject:self withKeyPath:@keypath(self.value) options:options];
+
+ // Keep the proxy alive as long as the target, or until the property subject
+ // terminates.
+ objc_setAssociatedObject(self.target, (__bridge void *)self, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ [[self.target rac_deallocDisposable] addDisposable:[RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ [self.channel.followingTerminal sendCompleted];
+ }]];
+
+ RACChannelTo(self, value, options[NSNullPlaceholderBindingOption]) = self.channel.followingTerminal;
+ return self;
+}
+
+- (void)dealloc {
+ [self.channel.followingTerminal sendCompleted];
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ target: %@, binding: %@ }", self.class, self, self.target, self.bindingName];
+}
+
+#pragma mark NSKeyValueObserving
+
++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
+ // Generating manual notifications for `value` is simpler and more
+ // performant than having KVO swizzle our class and add its own logic.
+ return NO;
+}
+
+@end
+
+@implementation NSObject (RACAppKitBindingsDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath {
+ [self rac_bind:binding toObject:object withKeyPath:keyPath nilValue:nil];
+}
+
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath nilValue:(id)nilValue {
+ [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, nilValue, NSNullPlaceholderBindingOption, nil]];
+}
+
+- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath transform:(id (^)(id value))transformBlock {
+ RACValueTransformer *transformer = [RACValueTransformer transformerWithBlock:transformBlock];
+ [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, transformer, NSValueTransformerBindingOption, nil]];
+}
+
+- (void)rac_bind:(NSString *)binding toObject:(id)object withNegatedKeyPath:(NSString *)keyPath {
+ [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, NSNegateBooleanTransformerName, NSValueTransformerNameBindingOption, nil]];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.h
new file mode 100644
index 0000000..1530eb4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.h
@@ -0,0 +1,34 @@
+//
+// NSObject+RACDeallocating.h
+// ReactiveCocoa
+//
+// Created by Kazuo Koga on 2013/03/15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACCompoundDisposable;
+@class RACDisposable;
+@class RACSignal;
+
+@interface NSObject (RACDeallocating)
+
+/// The compound disposable which will be disposed of when the receiver is
+/// deallocated.
+@property (atomic, readonly, strong) RACCompoundDisposable *rac_deallocDisposable;
+
+/// Returns a signal that will complete immediately before the receiver is fully
+/// deallocated. If already deallocated when the signal is subscribed to,
+/// a `completed` event will be sent immediately.
+- (RACSignal *)rac_willDeallocSignal;
+
+@end
+
+@interface NSObject (RACDeallocatingDeprecated)
+
+- (RACSignal *)rac_didDeallocSignal __attribute__((deprecated("Use -rac_willDeallocSignal")));
+
+- (void)rac_addDeallocDisposable:(RACDisposable *)disposable __attribute__((deprecated("Add disposables to -rac_deallocDisposable instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.m
new file mode 100644
index 0000000..a81d89c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.m
@@ -0,0 +1,129 @@
+//
+// NSObject+RACDeallocating.m
+// ReactiveCocoa
+//
+// Created by Kazuo Koga on 2013/03/15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACReplaySubject.h"
+#import <objc/message.h>
+#import <objc/runtime.h>
+
+static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable;
+
+static NSMutableSet *swizzledClasses() {
+ static dispatch_once_t onceToken;
+ static NSMutableSet *swizzledClasses = nil;
+ dispatch_once(&onceToken, ^{
+ swizzledClasses = [[NSMutableSet alloc] init];
+ });
+
+ return swizzledClasses;
+}
+
+static void swizzleDeallocIfNeeded(Class classToSwizzle) {
+ @synchronized (swizzledClasses()) {
+ NSString *className = NSStringFromClass(classToSwizzle);
+ if ([swizzledClasses() containsObject:className]) return;
+
+ SEL deallocSelector = sel_registerName("dealloc");
+
+ __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
+
+ id newDealloc = ^(__unsafe_unretained id self) {
+ RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
+ [compoundDisposable dispose];
+
+ if (originalDealloc == NULL) {
+ struct objc_super superInfo = {
+ .receiver = self,
+ .super_class = class_getSuperclass(classToSwizzle)
+ };
+
+ void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
+ msgSend(&superInfo, deallocSelector);
+ } else {
+ originalDealloc(self, deallocSelector);
+ }
+ };
+
+ IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
+
+ if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) {
+ // The class already contains a method implementation.
+ Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
+
+ // We need to store original implementation before setting new implementation
+ // in case method is called at the time of setting.
+ originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
+
+ // We need to store original implementation again, in case it just changed.
+ originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
+ }
+
+ [swizzledClasses() addObject:className];
+ }
+}
+
+@implementation NSObject (RACDeallocating)
+
+- (RACSignal *)rac_willDeallocSignal {
+ RACSignal *signal = objc_getAssociatedObject(self, _cmd);
+ if (signal != nil) return signal;
+
+ RACReplaySubject *subject = [RACReplaySubject subject];
+
+ [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ [subject sendCompleted];
+ }]];
+
+ objc_setAssociatedObject(self, _cmd, subject, OBJC_ASSOCIATION_RETAIN);
+
+ return subject;
+}
+
+- (RACCompoundDisposable *)rac_deallocDisposable {
+ @synchronized (self) {
+ RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
+ if (compoundDisposable != nil) return compoundDisposable;
+
+ swizzleDeallocIfNeeded(self.class);
+
+ compoundDisposable = [RACCompoundDisposable compoundDisposable];
+ objc_setAssociatedObject(self, RACObjectCompoundDisposable, compoundDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ return compoundDisposable;
+ }
+}
+
+@end
+
+@implementation NSObject (RACDeallocatingDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (RACSignal *)rac_didDeallocSignal {
+ RACSubject *subject = [RACSubject subject];
+
+ RACScopedDisposable *disposable = [[RACDisposable
+ disposableWithBlock:^{
+ [subject sendCompleted];
+ }]
+ asScopedDisposable];
+
+ objc_setAssociatedObject(self, (__bridge void *)disposable, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ return subject;
+}
+
+- (void)rac_addDeallocDisposable:(RACDisposable *)disposable {
+ [self.rac_deallocDisposable addDisposable:disposable];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.h
new file mode 100644
index 0000000..41e3d8a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.h
@@ -0,0 +1,21 @@
+//
+// NSObject+RACDescription.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// A private category providing a terser but faster alternative to -description.
+@interface NSObject (RACDescription)
+
+// A simplified description of the receiver, which does not invoke -description
+// (and thus should be much faster in many cases).
+//
+// This is for debugging purposes only, and will return a constant string
+// unless the RAC_DEBUG_SIGNAL_NAMES environment variable is set.
+- (NSString *)rac_description;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.m
new file mode 100644
index 0000000..0088880
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.m
@@ -0,0 +1,46 @@
+//
+// NSObject+RACDescription.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACDescription.h"
+#import "RACTuple.h"
+
+@implementation NSObject (RACDescription)
+
+- (NSString *)rac_description {
+ if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) {
+ return [[NSString alloc] initWithFormat:@"<%@: %p>", self.class, self];
+ } else {
+ return @"(description skipped)";
+ }
+}
+
+@end
+
+@implementation NSValue (RACDescription)
+
+- (NSString *)rac_description {
+ return self.description;
+}
+
+@end
+
+@implementation NSString (RACDescription)
+
+- (NSString *)rac_description {
+ return self.description;
+}
+
+@end
+
+@implementation RACTuple (RACDescription)
+
+- (NSString *)rac_description {
+ return self.allObjects.description;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.h
new file mode 100644
index 0000000..3b9ff2f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.h
@@ -0,0 +1,56 @@
+//
+// NSObject+RACKVOWrapper.h
+// GitHub
+//
+// Created by Josh Abernathy on 10/11/11.
+// Copyright (c) 2011 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+// RAC-specific KVO change dictionary key: Will be @YES if the change was caused
+// by the value at the key path or an intermediate value deallocating, @NO
+// otherwise.
+extern NSString * const RACKeyValueChangeCausedByDeallocationKey;
+
+// RAC-specific KVO change dictionary key: Will be @YES if the change only
+// affected the value of the last key path component leaving the values of the
+// intermediate key path components unaltered, @NO otherwise.
+extern NSString * const RACKeyValueChangeAffectedOnlyLastComponentKey;
+
+@class RACDisposable, RACKVOTrampoline;
+
+// A private category providing a block based interface to KVO.
+@interface NSObject (RACKVOWrapper)
+
+// Adds the given block as the callbacks for when the key path changes.
+//
+// Unlike direct KVO observation, this handles deallocation of `weak` properties
+// by generating an appropriate notification. This will only occur if there is
+// an `@property` declaration visible in the observed class, with the `weak`
+// memory management attribute.
+//
+// The observation does not need to be explicitly removed. It will be removed
+// when the observer or the receiver deallocate.
+//
+// keyPath - The key path to observe. Must not be nil.
+// options - The KVO observation options.
+// observer - The object that requested the observation. May be nil.
+// block - The block called when the value at the key path changes. It is
+// passed the current value of the key path and the extended KVO
+// change dictionary including RAC-specific keys and values. Must not
+// be nil.
+//
+// Returns a disposable that can be used to stop the observation.
+- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer block:(void (^)(id value, NSDictionary *change))block;
+
+@end
+
+typedef void (^RACKVOBlock)(id target, id observer, NSDictionary *change);
+
+@interface NSObject (RACKVOWrapperDeprecated)
+
+- (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block __attribute((deprecated("Use rac_observeKeyPath:options:observer:block: instead.")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.m
new file mode 100644
index 0000000..7f12312
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.m
@@ -0,0 +1,237 @@
+//
+// NSObject+RACKVOWrapper.m
+// GitHub
+//
+// Created by Josh Abernathy on 10/11/11.
+// Copyright (c) 2011 GitHub. All rights reserved.
+//
+
+#import "NSObject+RACKVOWrapper.h"
+#import "EXTRuntimeExtensions.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSString+RACKeyPathUtilities.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOTrampoline.h"
+#import "RACSerialDisposable.h"
+
+NSString * const RACKeyValueChangeCausedByDeallocationKey = @"RACKeyValueChangeCausedByDeallocationKey";
+NSString * const RACKeyValueChangeAffectedOnlyLastComponentKey = @"RACKeyValueChangeAffectedOnlyLastComponentKey";
+
+@implementation NSObject (RACKVOWrapper)
+
+- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer block:(void (^)(id, NSDictionary *))block {
+ NSCParameterAssert(block != nil);
+ NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0);
+
+ keyPath = [keyPath copy];
+
+ @unsafeify(observer);
+
+ NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
+ BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
+ NSString *keyPathHead = keyPathComponents[0];
+ NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;
+
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ // The disposable that groups all disposal necessary to clean up the callbacks
+ // added to the value of the first key path component.
+ RACSerialDisposable *firstComponentSerialDisposable = [RACSerialDisposable serialDisposableWithDisposable:[RACCompoundDisposable compoundDisposable]];
+ RACCompoundDisposable * (^firstComponentDisposable)(void) = ^{
+ return (RACCompoundDisposable *)firstComponentSerialDisposable.disposable;
+ };
+
+ [disposable addDisposable:firstComponentSerialDisposable];
+
+ // Adds the callback block to the value's deallocation. Also adds the logic to
+ // clean up the callback to the firstComponentDisposable.
+ void (^addDeallocObserverToPropertyValue)(NSObject *, NSString *, NSObject *) = ^(NSObject *parent, NSString *propertyKey, NSObject *value) {
+ // If a key path value is the observer, commonly when a key path begins
+ // with "self", we prevent deallocation triggered callbacks for any such key
+ // path components. Thus, the observer's deallocation is not considered a
+ // change to the key path.
+ @strongify(observer);
+ if (value == observer) return;
+
+ objc_property_t property = class_getProperty(object_getClass(parent), propertyKey.UTF8String);
+ if (property == NULL) {
+ // If we can't find an Objective-C property for this key, we assume
+ // that we don't need to observe its deallocation (thus matching
+ // vanilla KVO behavior).
+ //
+ // Even if we wanted to, there's not enough type information on
+ // ivars to figure out its memory management.
+ return;
+ }
+
+ rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
+ if (attributes == NULL) return;
+
+ @onExit {
+ free(attributes);
+ };
+
+ BOOL isNonObject = attributes->objectClass == nil && strstr(attributes->type, @encode(id)) != attributes->type;
+ BOOL isProtocol = attributes->objectClass == NSClassFromString(@"Protocol");
+ BOOL isBlock = strcmp(attributes->type, @encode(void(^)())) == 0;
+ if (isNonObject || isProtocol || isBlock) {
+ // If this property isn't actually an object (or is a Class object),
+ // no point in observing the deallocation of the wrapper returned by
+ // KVC.
+ return;
+ }
+
+ if (!attributes->weak) {
+ // If this property is an object, but not declared `weak`, we
+ // don't need to watch for it spontaneously being set to nil.
+ //
+ // Attempting to observe non-weak properties will result in
+ // broken behavior for dynamic getters, so don't even try.
+ return;
+ }
+
+ NSDictionary *change = @{
+ NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
+ NSKeyValueChangeNewKey: NSNull.null,
+ RACKeyValueChangeCausedByDeallocationKey: @YES,
+ RACKeyValueChangeAffectedOnlyLastComponentKey: @(keyPathHasOneComponent)
+ };
+
+ RACCompoundDisposable *valueDisposable = value.rac_deallocDisposable;
+ RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
+ block(nil, change);
+ }];
+
+ [valueDisposable addDisposable:deallocDisposable];
+ [firstComponentDisposable() addDisposable:[RACDisposable disposableWithBlock:^{
+ [valueDisposable removeDisposable:deallocDisposable];
+ }]];
+ };
+
+ // Adds the callback block to the remaining path components on the value. Also
+ // adds the logic to clean up the callbacks to the firstComponentDisposable.
+ void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
+ @strongify(observer);
+ RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:observer block:block];
+ [firstComponentDisposable() addDisposable:observerDisposable];
+ };
+
+ // Observe only the first key path component, when the value changes clean up
+ // the callbacks on the old value, add callbacks to the new value and call the
+ // callback block as needed.
+ //
+ // Note this does not use NSKeyValueObservingOptionInitial so this only
+ // handles changes to the value, callbacks to the initial value must be added
+ // separately.
+ NSKeyValueObservingOptions trampolineOptions = (options | NSKeyValueObservingOptionPrior) & ~NSKeyValueObservingOptionInitial;
+ RACKVOTrampoline *trampoline = [[RACKVOTrampoline alloc] initWithTarget:self observer:observer keyPath:keyPathHead options:trampolineOptions block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) {
+ // Prepare the change dictionary by adding the RAC specific keys
+ {
+ NSMutableDictionary *newChange = [change mutableCopy];
+ newChange[RACKeyValueChangeCausedByDeallocationKey] = @NO;
+ newChange[RACKeyValueChangeAffectedOnlyLastComponentKey] = @(keyPathHasOneComponent);
+ change = newChange.copy;
+ }
+
+ // If this is a prior notification, clean up all the callbacks added to the
+ // previous value and call the callback block. Everything else is deferred
+ // until after we get the notification after the change.
+ if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
+ [firstComponentDisposable() dispose];
+
+ if ((options & NSKeyValueObservingOptionPrior) != 0) {
+ block([trampolineTarget valueForKeyPath:keyPath], change);
+ }
+
+ return;
+ }
+
+ // From here the notification is not prior.
+ NSObject *value = [trampolineTarget valueForKey:keyPathHead];
+
+ // If the value has changed but is nil, there is no need to add callbacks to
+ // it, just call the callback block.
+ if (value == nil) {
+ block(nil, change);
+ return;
+ }
+
+ // From here the notification is not prior and the value is not nil.
+
+ // Create a new firstComponentDisposable while getting rid of the old one at
+ // the same time, in case this is being called concurrently.
+ RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]];
+ [oldFirstComponentDisposable dispose];
+
+ addDeallocObserverToPropertyValue(trampolineTarget, keyPathHead, value);
+
+ // If there are no further key path components, there is no need to add the
+ // other callbacks, just call the callback block with the value itself.
+ if (keyPathHasOneComponent) {
+ block(value, change);
+ return;
+ }
+
+ // The value has changed, is not nil, and there are more key path components
+ // to consider. Add the callbacks to the value for the remaining key path
+ // components and call the callback block with the current value of the full
+ // key path.
+ addObserverToValue(value);
+ block([value valueForKeyPath:keyPathTail], change);
+ }];
+
+ // Stop the KVO observation when this one is disposed of.
+ [disposable addDisposable:trampoline];
+
+ // Add the callbacks to the initial value if needed.
+ NSObject *value = [self valueForKey:keyPathHead];
+ if (value != nil) {
+ addDeallocObserverToPropertyValue(self, keyPathHead, value);
+
+ if (!keyPathHasOneComponent) {
+ addObserverToValue(value);
+ }
+ }
+
+ // Call the block with the initial value if needed.
+ if ((options & NSKeyValueObservingOptionInitial) != 0) {
+ id initialValue = [self valueForKeyPath:keyPath];
+ NSDictionary *initialChange = @{
+ NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
+ NSKeyValueChangeNewKey: initialValue ?: NSNull.null,
+ RACKeyValueChangeCausedByDeallocationKey: @NO,
+ RACKeyValueChangeAffectedOnlyLastComponentKey: @(keyPathHasOneComponent)
+ };
+ block(initialValue, initialChange);
+ }
+
+
+ RACCompoundDisposable *observerDisposable = observer.rac_deallocDisposable;
+ RACCompoundDisposable *selfDisposable = self.rac_deallocDisposable;
+ // Dispose of this observation if the receiver or the observer deallocate.
+ [observerDisposable addDisposable:disposable];
+ [selfDisposable addDisposable:disposable];
+
+ return [RACDisposable disposableWithBlock:^{
+ [disposable dispose];
+ [observerDisposable removeDisposable:disposable];
+ [selfDisposable removeDisposable:disposable];
+ }];
+}
+
+@end
+
+@implementation NSObject (RACKVOWrapperDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block {
+ return [[RACKVOTrampoline alloc] initWithTarget:self observer:observer keyPath:keyPath options:options block:block];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.h
new file mode 100644
index 0000000..0feb272
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.h
@@ -0,0 +1,55 @@
+//
+// NSObject+RACLifting.h
+// iOSDemo
+//
+// Created by Josh Abernathy on 10/13/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+@class RACSignal;
+
+@interface NSObject (RACLifting)
+
+/// Lifts the selector on the receiver into the reactive world. The selector will
+/// be invoked whenever any signal argument sends a value, but only after each
+/// signal has sent an initial value.
+///
+/// It will replay the most recently sent value to new subscribers.
+///
+/// This does not support C arrays or unions.
+///
+/// selector - The selector on self to invoke.
+/// firstSignal - The signal corresponding to the first method argument. This
+/// must not be nil.
+/// ... - A list of RACSignals corresponding to the remaining arguments.
+/// There must be a non-nil signal for each method argument.
+///
+/// Examples
+///
+/// [button rac_liftSelector:@selector(setTitleColor:forState:) withSignals:textColorSignal, [RACSignal return:@(UIControlStateNormal)], nil];
+///
+/// Returns a signal which sends the return value from each invocation of the
+/// selector. If the selector returns void, it instead sends RACUnit.defaultUnit.
+/// It completes only after all the signal arguments complete.
+- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... NS_REQUIRES_NIL_TERMINATION;
+
+/// Like -rac_liftSelector:withSignals:, but accepts an array instead of
+/// a variadic list of arguments.
+- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals;
+
+@end
+
+@interface NSObject (RACLiftingDeprecated)
+
+- (RACSignal *)rac_liftSelector:(SEL)selector withObjects:(id)arg, ... __attribute__((deprecated("Use -rac_liftSelector:withSignals: instead")));
+- (RACSignal *)rac_liftSelector:(SEL)selector withObjectsFromArray:(NSArray *)args __attribute__((deprecated("Use -rac_liftSelector:withSignalsFromArray: instead")));
+- (RACSignal *)rac_liftBlock:(id)block withArguments:(id)arg, ... NS_REQUIRES_NIL_TERMINATION __attribute__((deprecated("Use +combineLatest:reduce: instead")));
+- (RACSignal *)rac_liftBlock:(id)block withArgumentsFromArray:(NSArray *)args __attribute__((deprecated("Use +combineLatest:reduce: instead")));
+
+@end
+
+@interface NSObject (RACLiftingUnavailable)
+
+- (instancetype)rac_lift __attribute__((unavailable("Use -rac_liftSelector:withSignals: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.m
new file mode 100644
index 0000000..96fea76
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACLifting.m
@@ -0,0 +1,132 @@
+//
+// NSObject+RACLifting.m
+// iOSDemo
+//
+// Created by Josh Abernathy on 10/13/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACLifting.h"
+#import "EXTScope.h"
+#import "NSInvocation+RACTypeParsing.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACSignal+Operations.h"
+#import "RACTuple.h"
+#import "NSObject+RACDescription.h"
+
+@implementation NSObject (RACLifting)
+
+- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals {
+ NSCParameterAssert(selector != NULL);
+ NSCParameterAssert(signals != nil);
+ NSCParameterAssert(signals.count > 0);
+
+ NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
+ NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector));
+
+ NSUInteger numberOfArguments __attribute__((unused)) = methodSignature.numberOfArguments - 2;
+ NSCAssert(numberOfArguments == signals.count, @"Wrong number of signals for %@ (expected %lu, got %lu)", NSStringFromSelector(selector), (unsigned long)numberOfArguments, (unsigned long)signals.count);
+
+ @unsafeify(self);
+
+ return [[[[[RACSignal
+ combineLatest:signals]
+ takeUntil:self.rac_willDeallocSignal]
+ map:^(RACTuple *arguments) {
+ @strongify(self);
+
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
+ invocation.selector = selector;
+ invocation.rac_argumentsTuple = arguments;
+ [invocation invokeWithTarget:self];
+
+ return invocation.rac_returnValue;
+ }]
+ replayLast]
+ setNameWithFormat:@"%@ -rac_liftSelector: %s withSignalsFromArray: %@", [self rac_description], sel_getName(selector), signals];
+}
+
+- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... {
+ NSCParameterAssert(firstSignal != nil);
+
+ NSMutableArray *signals = [NSMutableArray array];
+
+ va_list args;
+ va_start(args, firstSignal);
+ for (id currentSignal = firstSignal; currentSignal != nil; currentSignal = va_arg(args, id)) {
+ NSCAssert([currentSignal isKindOfClass:RACSignal.class], @"Argument %@ is not a RACSignal", currentSignal);
+
+ [signals addObject:currentSignal];
+ }
+ va_end(args);
+
+ return [[self
+ rac_liftSelector:selector withSignalsFromArray:signals]
+ setNameWithFormat:@"%@ -rac_liftSelector: %s withSignals: %@", [self rac_description], sel_getName(selector), signals];
+}
+
+@end
+
+@implementation NSObject (RACLiftingDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+static NSArray *RACMapArgumentsToSignals(NSArray *args) {
+ NSMutableArray *mappedArgs = [NSMutableArray array];
+ for (id arg in args) {
+ if ([arg isEqual:RACTupleNil.tupleNil]) {
+ [mappedArgs addObject:[RACSignal return:nil]];
+ } else if ([arg isKindOfClass:RACSignal.class]) {
+ [mappedArgs addObject:arg];
+ } else {
+ [mappedArgs addObject:[RACSignal return:arg]];
+ }
+ }
+
+ return mappedArgs;
+}
+
+- (RACSignal *)rac_liftSelector:(SEL)selector withObjects:(id)arg, ... {
+ NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
+ NSMutableArray *arguments = [NSMutableArray array];
+
+ va_list args;
+ va_start(args, arg);
+ for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) {
+ id currentObject = (i == 2 ? arg : va_arg(args, id));
+ [arguments addObject:currentObject ?: RACTupleNil.tupleNil];
+ }
+
+ va_end(args);
+ return [self rac_liftSelector:selector withObjectsFromArray:arguments];
+}
+
+- (RACSignal *)rac_liftSelector:(SEL)selector withObjectsFromArray:(NSArray *)args {
+ return [self rac_liftSelector:selector withSignalsFromArray:RACMapArgumentsToSignals(args)];
+}
+
+- (RACSignal *)rac_liftBlock:(id)block withArguments:(id)arg, ... {
+ NSMutableArray *arguments = [NSMutableArray array];
+
+ va_list args;
+ va_start(args, arg);
+ for (id currentObject = arg; currentObject != nil; currentObject = va_arg(args, id)) {
+ [arguments addObject:currentObject];
+ }
+
+ va_end(args);
+ return [self rac_liftBlock:block withArgumentsFromArray:arguments];
+}
+
+- (RACSignal *)rac_liftBlock:(id)block withArgumentsFromArray:(NSArray *)args {
+ return [[[[RACSignal
+ combineLatest:RACMapArgumentsToSignals(args)]
+ reduceEach:block]
+ takeUntil:self.rac_willDeallocSignal]
+ replayLast];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.h
new file mode 100644
index 0000000..2f55195
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.h
@@ -0,0 +1,102 @@
+//
+// NSObject+RACPropertySubscribing.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "EXTKeyPathCoding.h"
+#import "metamacros.h"
+
+/// Creates a signal which observes `KEYPATH` on `TARGET` for changes.
+///
+/// In either case, the observation continues until `TARGET` _or self_ is
+/// deallocated. If any intermediate object is deallocated instead, it will be
+/// assumed to have been set to nil.
+///
+/// Make sure to `@strongify(self)` when using this macro within a block! The
+/// macro will _always_ reference `self`, which can silently introduce a retain
+/// cycle within a block. As a result, you should make sure that `self` is a weak
+/// reference (e.g., created by `@weakify` and `@strongify`) before the
+/// expression that uses `RACObserve`.
+///
+/// Examples
+///
+/// // Observes self, and doesn't stop until self is deallocated.
+/// RACSignal *selfSignal = RACObserve(self, arrayController.items);
+///
+/// // Observes the array controller, and stops when self _or_ the array
+/// // controller is deallocated.
+/// RACSignal *arrayControllerSignal = RACObserve(self.arrayController, items);
+///
+/// // Observes obj.arrayController, and stops when self _or_ the array
+/// // controller is deallocated.
+/// RACSignal *signal2 = RACObserve(obj.arrayController, items);
+///
+/// @weakify(self);
+/// RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
+/// // Avoids a retain cycle because of RACObserve implicitly referencing
+/// // self.
+/// @strongify(self);
+/// return RACObserve(arrayController, items);
+/// }];
+///
+/// Returns a signal which sends the current value of the key path on
+/// subscription, then sends the new value every time it changes, and sends
+/// completed if self or observer is deallocated.
+#define RACObserve(TARGET, KEYPATH) \
+ [(id)(TARGET) rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]
+
+@class RACDisposable;
+@class RACSignal;
+
+@interface NSObject (RACPropertySubscribing)
+
+/// Creates a signal to observe the value at the given key path.
+///
+/// The initial value is sent on subscription, the subsequent values are sent
+/// from whichever thread the change occured on, even if it doesn't have a valid
+/// scheduler.
+///
+/// Returns a signal that immediately sends the receiver's current value at the
+/// given keypath, then any changes thereafter.
+- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer;
+
+/// Creates a signal to observe the changes of the given key path.
+///
+/// The initial value is sent on subscription, the subsequent values are sent
+/// from whichever thread the change occured on, even if it doesn't have a valid
+/// scheduler.
+///
+/// Returns a signal that sends tuples containing the current value at the key
+/// path and the change dictionary for each KVO callback.
+- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer;
+
+@end
+
+#define RACAble(...) \
+ metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
+ (_RACAbleObject(self, __VA_ARGS__)) \
+ (_RACAbleObject(__VA_ARGS__))
+
+#define _RACAbleObject(object, property) [object rac_signalForKeyPath:@keypath(object, property) observer:self]
+
+#define RACAbleWithStart(...) \
+ metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
+ (_RACAbleWithStartObject(self, __VA_ARGS__)) \
+ (_RACAbleWithStartObject(__VA_ARGS__))
+
+#define _RACAbleWithStartObject(object, property) [object rac_signalWithStartingValueForKeyPath:@keypath(object, property) observer:self]
+
+@interface NSObject (RACPropertySubscribingDeprecated)
+
++ (RACSignal *)rac_signalFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((deprecated("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
++ (RACSignal *)rac_signalWithStartingValueFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((deprecated("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
++ (RACSignal *)rac_signalWithChangesFor:(NSObject *)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer __attribute__((deprecated("Use -rac_valuesAndChangesForKeyPath:options:observer: instead.")));
+- (RACSignal *)rac_signalForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((deprecated("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
+- (RACSignal *)rac_signalWithStartingValueForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((deprecated("Use -rac_valuesForKeyPath:observer: or RACObserve() instead.")));
+- (RACDisposable *)rac_deriveProperty:(NSString *)keyPath from:(RACSignal *)signal __attribute__((deprecated("Use -[RACSignal setKeyPath:onObject:] instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.m
new file mode 100644
index 0000000..edd2acb
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.m
@@ -0,0 +1,161 @@
+//
+// NSObject+RACPropertySubscribing.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACPropertySubscribing.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACKVOWrapper.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOTrampoline.h"
+#import "RACSubscriber.h"
+#import "RACSignal+Operations.h"
+#import "RACTuple.h"
+#import <libkern/OSAtomic.h>
+
+@implementation NSObject (RACPropertySubscribing)
+
+- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
+ return [[[self
+ rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
+ reduceEach:^(id value, NSDictionary *change) {
+ return value;
+ }]
+ setNameWithFormat:@"RACObserve(%@, %@)", self.rac_description, keyPath];
+}
+
+- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer {
+ keyPath = [keyPath copy];
+
+ NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
+ objectLock.name = @"com.github.ReactiveCocoa.NSObjectRACPropertySubscribing";
+
+ __block __unsafe_unretained NSObject *unsafeSelf = self;
+ __block __unsafe_unretained NSObject *unsafeObserver = observer;
+
+ RACSignal *deallocSignal = [[RACSignal
+ zip:@[
+ self.rac_willDeallocSignal,
+ observer.rac_willDeallocSignal ?: [RACSignal never]
+ ]]
+ doCompleted:^{
+ // Forces deallocation to wait if the object variables are currently
+ // being read on another thread.
+ [objectLock lock];
+ @onExit {
+ [objectLock unlock];
+ };
+
+ unsafeSelf = nil;
+ unsafeObserver = nil;
+ }];
+
+ return [[[RACSignal
+ createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ // Hold onto the lock the whole time we're setting up the KVO
+ // observation, because any resurrection that might be caused by our
+ // retaining below must be balanced out by the time -dealloc returns
+ // (if another thread is waiting on the lock above).
+ [objectLock lock];
+ @onExit {
+ [objectLock unlock];
+ };
+
+ __strong NSObject *observer __attribute__((objc_precise_lifetime)) = unsafeObserver;
+ __strong NSObject *self __attribute__((objc_precise_lifetime)) = unsafeSelf;
+
+ if (self == nil) {
+ [subscriber sendCompleted];
+ return nil;
+ }
+
+ return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change) {
+ [subscriber sendNext:RACTuplePack(value, change)];
+ }];
+ }]
+ takeUntil:deallocSignal]
+ setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", self.rac_description, keyPath, (unsigned long)options, observer.rac_description];
+}
+
+@end
+
+static RACSignal *signalWithoutChangesFor(Class class, NSObject *object, NSString *keyPath, NSKeyValueObservingOptions options, NSObject *observer) {
+ NSCParameterAssert(object != nil);
+ NSCParameterAssert(keyPath != nil);
+ NSCParameterAssert(observer != nil);
+
+ keyPath = [keyPath copy];
+
+ @unsafeify(object);
+
+ return [[class
+ rac_signalWithChangesFor:object keyPath:keyPath options:options observer:observer]
+ map:^(NSDictionary *change) {
+ @strongify(object);
+ return [object valueForKeyPath:keyPath];
+ }];
+}
+
+@implementation NSObject (RACPropertySubscribingDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
++ (RACSignal *)rac_signalFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer {
+ return signalWithoutChangesFor(self, object, keyPath, 0, observer);
+}
+
++ (RACSignal *)rac_signalWithStartingValueFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer {
+ return signalWithoutChangesFor(self, object, keyPath, NSKeyValueObservingOptionInitial, observer);
+}
+
++ (RACSignal *)rac_signalWithChangesFor:(NSObject *)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer {
+ @unsafeify(observer, object);
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+
+ @strongify(observer, object);
+ RACKVOTrampoline *KVOTrampoline = [object rac_addObserver:observer forKeyPath:keyPath options:options block:^(id target, id observer, NSDictionary *change) {
+ [subscriber sendNext:change];
+ }];
+
+ @weakify(subscriber);
+ RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
+ @strongify(subscriber);
+ [KVOTrampoline dispose];
+ [subscriber sendCompleted];
+ }];
+
+ [observer.rac_deallocDisposable addDisposable:deallocDisposable];
+ [object.rac_deallocDisposable addDisposable:deallocDisposable];
+
+ RACCompoundDisposable *observerDisposable = observer.rac_deallocDisposable;
+ RACCompoundDisposable *objectDisposable = object.rac_deallocDisposable;
+ return [RACDisposable disposableWithBlock:^{
+ [observerDisposable removeDisposable:deallocDisposable];
+ [objectDisposable removeDisposable:deallocDisposable];
+ [KVOTrampoline dispose];
+ }];
+ }] setNameWithFormat:@"RACAble(%@, %@)", object.rac_description, keyPath];
+}
+
+- (RACSignal *)rac_signalForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
+ return [self.class rac_signalFor:self keyPath:keyPath observer:observer];
+}
+
+- (RACSignal *)rac_signalWithStartingValueForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
+ return [self.class rac_signalWithStartingValueFor:self keyPath:keyPath observer:observer];
+}
+
+- (RACDisposable *)rac_deriveProperty:(NSString *)keyPath from:(RACSignal *)signal {
+ return [signal setKeyPath:keyPath onObject:self];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h
new file mode 100644
index 0000000..c6f3de5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h
@@ -0,0 +1,79 @@
+//
+// NSObject+RACSelectorSignal.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+/// The domain for any errors originating from -rac_signalForSelector:.
+extern NSString * const RACSelectorSignalErrorDomain;
+
+/// -rac_signalForSelector: was going to add a new method implementation for
+/// `selector`, but another thread added an implementation before it was able to.
+///
+/// This will _not_ occur for cases where a method implementation exists before
+/// -rac_signalForSelector: is invoked.
+extern const NSInteger RACSelectorSignalErrorMethodSwizzlingRace;
+
+@interface NSObject (RACSelectorSignal)
+
+/// Creates a signal associated with the receiver, which will send a tuple of the
+/// method's arguments each time the given selector is invoked.
+///
+/// If the selector is already implemented on the receiver, the existing
+/// implementation will be invoked _before_ the signal fires.
+///
+/// If the selector is not yet implemented on the receiver, the injected
+/// implementation will have a `void` return type and accept only object
+/// arguments. Invoking the added implementation with non-object values, or
+/// expecting a return value, will result in undefined behavior.
+///
+/// This is useful for changing an event or delegate callback into a signal. For
+/// example, on an NSView:
+///
+/// [[view rac_signalForSelector:@selector(mouseDown:)] subscribeNext:^(RACTuple *args) {
+/// NSEvent *event = args.first;
+/// NSLog(@"mouse button pressed: %@", event);
+/// }];
+///
+/// selector - The selector for whose invocations are to be observed. If it
+/// doesn't exist, it will be implemented to accept object arguments
+/// and return void. This cannot have C arrays or unions as arguments
+/// or C arrays, unions, structs, complex or vector types as return
+/// type.
+///
+/// Returns a signal which will send a tuple of arguments upon each invocation of
+/// the selector, then completes when the receiver is deallocated. `next` events
+/// will be sent synchronously from the thread that invoked the method. If
+/// a runtime call fails, the signal will send an error in the
+/// RACSelectorSignalErrorDomain.
+- (RACSignal *)rac_signalForSelector:(SEL)selector;
+
+/// Behaves like -rac_signalForSelector:, but if the selector is not yet
+/// implemented on the receiver, its method signature is looked up within
+/// `protocol`, and may accept non-object arguments.
+///
+/// If the selector is not yet implemented and has a return value, the injected
+/// method will return all zero bits (equal to `nil`, `NULL`, 0, 0.0f, etc.).
+///
+/// selector - The selector for whose invocations are to be observed. If it
+/// doesn't exist, it will be implemented using information from
+/// `protocol`, and may accept non-object arguments and return
+/// a value. This cannot have C arrays or unions as arguments or
+/// return type.
+/// protocol - The protocol in which `selector` is declared. This will be used
+/// for type information if the selector is not already implemented on
+/// the receiver. This must not be `NULL`, and `selector` must exist
+/// in this protocol.
+///
+/// Returns a signal which will send a tuple of arguments on each invocation of
+/// the selector, or an error in RACSelectorSignalErrorDomain if a runtime
+/// call fails.
+- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.m
new file mode 100644
index 0000000..4c3aab9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.m
@@ -0,0 +1,322 @@
+//
+// NSObject+RACSelectorSignal.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACSelectorSignal.h"
+#import "EXTRuntimeExtensions.h"
+#import "NSInvocation+RACTypeParsing.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACObjCRuntime.h"
+#import "RACSubject.h"
+#import "RACTuple.h"
+#import "NSObject+RACDescription.h"
+#import <objc/message.h>
+#import <objc/runtime.h>
+
+NSString * const RACSelectorSignalErrorDomain = @"RACSelectorSignalErrorDomain";
+const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1;
+
+static NSString * const RACSignalForSelectorAliasPrefix = @"rac_alias_";
+static NSString * const RACSubclassSuffix = @"_RACSelectorSignal";
+
+static NSMutableSet *swizzledClasses() {
+ static NSMutableSet *set;
+ static dispatch_once_t pred;
+
+ dispatch_once(&pred, ^{
+ set = [[NSMutableSet alloc] init];
+ });
+
+ return set;
+}
+
+@implementation NSObject (RACSelectorSignal)
+
+static BOOL RACForwardInvocation(id self, NSInvocation *invocation) {
+ SEL aliasSelector = RACAliasForSelector(invocation.selector);
+ RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
+
+ Class class = object_getClass(invocation.target);
+ BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector];
+ if (respondsToAlias) {
+ invocation.selector = aliasSelector;
+ [invocation invoke];
+ }
+
+ if (subject == nil) return respondsToAlias;
+
+ [subject sendNext:invocation.rac_argumentsTuple];
+ return YES;
+}
+
+static void RACSwizzleForwardInvocation(Class class) {
+ SEL forwardInvocationSEL = @selector(forwardInvocation:);
+ Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);
+
+ // Preserve any existing implementation of -forwardInvocation:.
+ void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
+ if (forwardInvocationMethod != NULL) {
+ originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod);
+ }
+
+ // Set up a new version of -forwardInvocation:.
+ //
+ // If the selector has been passed to -rac_signalForSelector:, invoke
+ // the aliased method, and forward the arguments to any attached signals.
+ //
+ // If the selector has not been passed to -rac_signalForSelector:,
+ // invoke any existing implementation of -forwardInvocation:. If there
+ // was no existing implementation, throw an unrecognized selector
+ // exception.
+ id newForwardInvocation = ^(id self, NSInvocation *invocation) {
+ BOOL matched = RACForwardInvocation(self, invocation);
+ if (matched) return;
+
+ if (originalForwardInvocation == NULL) {
+ [self doesNotRecognizeSelector:invocation.selector];
+ } else {
+ originalForwardInvocation(self, forwardInvocationSEL, invocation);
+ }
+ };
+
+ class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@");
+}
+
+static void RACSwizzleRespondsToSelector(Class class) {
+ SEL respondsToSelectorSEL = @selector(respondsToSelector:);
+
+ // Preserve existing implementation of -respondsToSelector:.
+ Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL);
+ BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod);
+
+ // Set up a new version of -respondsToSelector: that returns YES for methods
+ // added by -rac_signalForSelector:.
+ //
+ // If the selector has a method defined on the receiver's actual class, and
+ // if that method's implementation is _objc_msgForward, then returns whether
+ // the instance has a signal for the selector.
+ // Otherwise, call the original -respondsToSelector:.
+ id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
+ Method method = rac_getImmediateInstanceMethod(object_getClass(self), selector);
+
+ if (method != NULL && method_getImplementation(method) == _objc_msgForward) {
+ SEL aliasSelector = RACAliasForSelector(selector);
+ return objc_getAssociatedObject(self, aliasSelector) != nil;
+ }
+
+ return originalRespondsToSelector(self, respondsToSelectorSEL, selector);
+ };
+
+ class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod));
+}
+
+static void RACSwizzleGetClass(Class class, Class statedClass) {
+ SEL selector = @selector(class);
+ Method method = class_getInstanceMethod(class, selector);
+ IMP newIMP = imp_implementationWithBlock(^(id self) {
+ return statedClass;
+ });
+ class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method));
+}
+
+static void RACSwizzleMethodSignatureForSelector(Class class) {
+ IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) {
+ // Don't send the -class message to the receiver because we've changed
+ // that to return the original class.
+ Class actualClass = object_getClass(self);
+ Method method = class_getInstanceMethod(actualClass, selector);
+ if (method == NULL) {
+ // Messages that the original class dynamically implements fall
+ // here.
+ //
+ // Call the original class' -methodSignatureForSelector:.
+ struct objc_super target = {
+ .super_class = class_getSuperclass(class),
+ .receiver = self,
+ };
+ NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper;
+ return messageSend(&target, @selector(methodSignatureForSelector:), selector);
+ }
+
+ char const *encoding = method_getTypeEncoding(method);
+ return [NSMethodSignature signatureWithObjCTypes:encoding];
+ });
+
+ SEL selector = @selector(methodSignatureForSelector:);
+ Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector);
+ class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod));
+}
+
+// It's hard to tell which struct return types use _objc_msgForward, and
+// which use _objc_msgForward_stret instead, so just exclude all struct, array,
+// union, complex and vector return types.
+static void RACCheckTypeEncoding(const char *typeEncoding) {
+#if !NS_BLOCK_ASSERTIONS
+ // Some types, including vector types, are not encoded. In these cases the
+ // signature starts with the size of the argument frame.
+ NSCAssert(*typeEncoding < '1' || *typeEncoding > '9', @"unknown method return type not supported in type encoding: %s", typeEncoding);
+ NSCAssert(strstr(typeEncoding, "(") != typeEncoding, @"union method return type not supported");
+ NSCAssert(strstr(typeEncoding, "{") != typeEncoding, @"struct method return type not supported");
+ NSCAssert(strstr(typeEncoding, "[") != typeEncoding, @"array method return type not supported");
+ NSCAssert(strstr(typeEncoding, @encode(_Complex float)) != typeEncoding, @"complex float method return type not supported");
+ NSCAssert(strstr(typeEncoding, @encode(_Complex double)) != typeEncoding, @"complex double method return type not supported");
+ NSCAssert(strstr(typeEncoding, @encode(_Complex long double)) != typeEncoding, @"complex long double method return type not supported");
+
+#endif // !NS_BLOCK_ASSERTIONS
+}
+
+static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
+ SEL aliasSelector = RACAliasForSelector(selector);
+
+ @synchronized (self) {
+ RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
+ if (subject != nil) return subject;
+
+ Class class = RACSwizzleClass(self);
+ NSCAssert(class != nil, @"Could not swizzle class of %@", self);
+
+ subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", self.rac_description, sel_getName(selector)];
+ objc_setAssociatedObject(self, aliasSelector, subject, OBJC_ASSOCIATION_RETAIN);
+
+ [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ [subject sendCompleted];
+ }]];
+
+ Method targetMethod = class_getInstanceMethod(class, selector);
+ if (targetMethod == NULL) {
+ const char *typeEncoding;
+ if (protocol == NULL) {
+ typeEncoding = RACSignatureForUndefinedSelector(selector);
+ } else {
+ // Look for the selector as an optional instance method.
+ struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
+
+ if (methodDescription.name == NULL) {
+ // Then fall back to looking for a required instance
+ // method.
+ methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES);
+ NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol));
+ }
+
+ typeEncoding = methodDescription.types;
+ }
+
+ RACCheckTypeEncoding(typeEncoding);
+
+ // Define the selector to call -forwardInvocation:.
+ if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"A race condition occurred implementing %@ on class %@", nil), NSStringFromSelector(selector), class],
+ NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Invoke -rac_signalForSelector: again to override the implementation.", nil)
+ };
+
+ return [RACSignal error:[NSError errorWithDomain:RACSelectorSignalErrorDomain code:RACSelectorSignalErrorMethodSwizzlingRace userInfo:userInfo]];
+ }
+ } else if (method_getImplementation(targetMethod) != _objc_msgForward) {
+ // Make a method alias for the existing method implementation.
+ const char *typeEncoding = method_getTypeEncoding(targetMethod);
+
+ RACCheckTypeEncoding(typeEncoding);
+
+ BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
+ NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class);
+
+ // Redefine the selector to call -forwardInvocation:.
+ class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
+ }
+
+ return subject;
+ }
+}
+
+static SEL RACAliasForSelector(SEL originalSelector) {
+ NSString *selectorName = NSStringFromSelector(originalSelector);
+ return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]);
+}
+
+static const char *RACSignatureForUndefinedSelector(SEL selector) {
+ const char *name = sel_getName(selector);
+ NSMutableString *signature = [NSMutableString stringWithString:@"v@:"];
+
+ while ((name = strchr(name, ':')) != NULL) {
+ [signature appendString:@"@"];
+ name++;
+ }
+
+ return signature.UTF8String;
+}
+
+static Class RACSwizzleClass(NSObject *self) {
+ Class statedClass = self.class;
+ Class baseClass = object_getClass(self);
+ NSString *className = NSStringFromClass(baseClass);
+
+ if ([className hasSuffix:RACSubclassSuffix]) {
+ return baseClass;
+ } else if (statedClass != baseClass) {
+ // If the class is already lying about what it is, it's probably a KVO
+ // dynamic subclass or something else that we shouldn't subclass
+ // ourselves.
+ //
+ // Just swizzle -forwardInvocation: in-place. Since the object's class
+ // was almost certainly dynamically changed, we shouldn't see another of
+ // these classes in the hierarchy.
+ //
+ // Additionally, swizzle -respondsToSelector: because the default
+ // implementation may be ignorant of methods added to this class.
+ @synchronized (swizzledClasses()) {
+ if (![swizzledClasses() containsObject:className]) {
+ RACSwizzleForwardInvocation(baseClass);
+ RACSwizzleRespondsToSelector(baseClass);
+ RACSwizzleGetClass(baseClass, statedClass);
+ RACSwizzleGetClass(object_getClass(baseClass), statedClass);
+ RACSwizzleMethodSignatureForSelector(baseClass);
+ [swizzledClasses() addObject:className];
+ }
+ }
+
+ return baseClass;
+ }
+
+ const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String;
+ Class subclass = objc_getClass(subclassName);
+
+ if (subclass == nil) {
+ subclass = [RACObjCRuntime createClass:subclassName inheritingFromClass:baseClass];
+ if (subclass == nil) return nil;
+
+ RACSwizzleForwardInvocation(subclass);
+ RACSwizzleRespondsToSelector(subclass);
+
+ RACSwizzleGetClass(subclass, statedClass);
+ RACSwizzleGetClass(object_getClass(subclass), statedClass);
+
+ RACSwizzleMethodSignatureForSelector(subclass);
+
+ objc_registerClassPair(subclass);
+ }
+
+ object_setClass(self, subclass);
+ return subclass;
+}
+
+- (RACSignal *)rac_signalForSelector:(SEL)selector {
+ NSCParameterAssert(selector != NULL);
+
+ return NSObjectRACSignalForSelector(self, selector, NULL);
+}
+
+- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol {
+ NSCParameterAssert(selector != NULL);
+ NSCParameterAssert(protocol != NULL);
+
+ return NSObjectRACSignalForSelector(self, selector, protocol);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.h
new file mode 100644
index 0000000..8bea2db
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.h
@@ -0,0 +1,20 @@
+//
+// NSOrderedSet+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSOrderedSet (RACSequenceAdditions)
+
+/// Creates and returns a sequence corresponding to the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.m
new file mode 100644
index 0000000..55dfd0b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.m
@@ -0,0 +1,19 @@
+//
+// NSOrderedSet+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSOrderedSet+RACSequenceAdditions.h"
+#import "NSArray+RACSequenceAdditions.h"
+
+@implementation NSOrderedSet (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ // TODO: First class support for ordered set sequences.
+ return self.array.rac_sequence;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.h
new file mode 100644
index 0000000..6665501
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.h
@@ -0,0 +1,20 @@
+//
+// NSSet+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSSet (RACSequenceAdditions)
+
+/// Creates and returns a sequence corresponding to the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.m
new file mode 100644
index 0000000..cc07f75
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.m
@@ -0,0 +1,19 @@
+//
+// NSSet+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSSet+RACSequenceAdditions.h"
+#import "NSArray+RACSequenceAdditions.h"
+
+@implementation NSSet (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ // TODO: First class support for set sequences.
+ return self.allObjects.rac_sequence;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.h b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.h
new file mode 100644
index 0000000..d56cf59
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.h
@@ -0,0 +1,34 @@
+//
+// NSString+RACKeyPathUtilities.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 05/05/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// A private category of methods to extract parts of a key path.
+@interface NSString (RACKeyPathUtilities)
+
+// Returns an array of the components of the receiver.
+//
+// Calling this method on a string that isn't a key path is considered undefined
+// behavior.
+- (NSArray *)rac_keyPathComponents;
+
+// Returns a key path with all the components of the receiver except for the
+// last one.
+//
+// Calling this method on a string that isn't a key path is considered undefined
+// behavior.
+- (NSString *)rac_keyPathByDeletingLastKeyPathComponent;
+
+// Returns a key path with all the components of the receiver expect for the
+// first one.
+//
+// Calling this method on a string that isn't a key path is considered undefined
+// behavior.
+- (NSString *)rac_keyPathByDeletingFirstKeyPathComponent;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.m b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.m
new file mode 100644
index 0000000..63a100c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.m
@@ -0,0 +1,36 @@
+//
+// NSString+RACKeyPathUtilities.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 05/05/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSString+RACKeyPathUtilities.h"
+
+@implementation NSString (RACKeyPathUtilities)
+
+- (NSArray *)rac_keyPathComponents {
+ if (self.length == 0) {
+ return nil;
+ }
+ return [self componentsSeparatedByString:@"."];
+}
+
+- (NSString *)rac_keyPathByDeletingLastKeyPathComponent {
+ NSUInteger lastDotIndex = [self rangeOfString:@"." options:NSBackwardsSearch].location;
+ if (lastDotIndex == NSNotFound) {
+ return nil;
+ }
+ return [self substringToIndex:lastDotIndex];
+}
+
+- (NSString *)rac_keyPathByDeletingFirstKeyPathComponent {
+ NSUInteger firstDotIndex = [self rangeOfString:@"."].location;
+ if (firstDotIndex == NSNotFound) {
+ return nil;
+ }
+ return [self substringFromIndex:firstDotIndex + 1];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.h b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.h
new file mode 100644
index 0000000..0116231
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.h
@@ -0,0 +1,21 @@
+//
+// NSString+RACSequenceAdditions.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSequence;
+
+@interface NSString (RACSequenceAdditions)
+
+/// Creates and returns a sequence containing strings corresponding to each
+/// composed character sequence in the receiver.
+///
+/// Mutating the receiver will not affect the sequence after it's been created.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.m b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.m
new file mode 100644
index 0000000..eb6a76e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.m
@@ -0,0 +1,18 @@
+//
+// NSString+RACSequenceAdditions.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "NSString+RACSequenceAdditions.h"
+#import "RACStringSequence.h"
+
+@implementation NSString (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ return [RACStringSequence sequenceWithString:self offset:0];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.h
new file mode 100644
index 0000000..d904a4c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.h
@@ -0,0 +1,22 @@
+//
+// NSString+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+@class RACScheduler;
+
+@interface NSString (RACSupport)
+
+// Reads in the contents of the file using +[NSString stringWithContentsOfURL:usedEncoding:error:].
+// Note that encoding won't be valid until the signal completes successfully.
+//
+// scheduler - cannot be nil.
++ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.m
new file mode 100644
index 0000000..c6ebe0b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.m
@@ -0,0 +1,35 @@
+//
+// NSString+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSString+RACSupport.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+
+@implementation NSString (RACSupport)
+
++ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler {
+ NSCParameterAssert(scheduler != nil);
+
+ RACReplaySubject *subject = [RACReplaySubject subject];
+ [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ usedEncoding:scheduler: %@", URL, scheduler];
+
+ [scheduler schedule:^{
+ NSError *error = nil;
+ NSString *string = [NSString stringWithContentsOfURL:URL usedEncoding:encoding error:&error];
+ if(string == nil) {
+ [subject sendError:error];
+ } else {
+ [subject sendNext:string];
+ [subject sendCompleted];
+ }
+ }];
+
+ return subject;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.h
new file mode 100644
index 0000000..e3fc8ed
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.h
@@ -0,0 +1,19 @@
+//
+// NSText+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-03-08.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class RACSignal;
+
+@interface NSText (RACSignalSupport)
+
+/// Returns a signal which sends the current `string` of the receiver, then the
+/// new value any time it changes.
+- (RACSignal *)rac_textSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.m
new file mode 100644
index 0000000..7e63f2a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.m
@@ -0,0 +1,38 @@
+//
+// NSText+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-03-08.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSText+RACSignalSupport.h"
+#import "EXTScope.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+#import "NSObject+RACDescription.h"
+
+@implementation NSText (RACSignalSupport)
+
+- (RACSignal *)rac_textSignal {
+ @unsafeify(self);
+ return [[[[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ @strongify(self);
+ id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) {
+ [subscriber sendNext:note.object];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [NSNotificationCenter.defaultCenter removeObserver:observer];
+ }];
+ }]
+ map:^(NSText *text) {
+ return [text.string copy];
+ }]
+ startWith:[self.string copy]]
+ setNameWithFormat:@"%@ -rac_textSignal", [self rac_description]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.h
new file mode 100644
index 0000000..e9bf04f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.h
@@ -0,0 +1,25 @@
+//
+// NSURLConnection+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+@interface NSURLConnection (RACSupport)
+
+// Lazily loads data for the given request in the background.
+//
+// request - The URL request to load. This must not be nil.
+//
+// Returns a signal which will begin loading the request upon each subscription,
+// then send a `RACTuple` of the received `NSURLResponse` and downloaded
+// `NSData`, and complete on a background thread. If any errors occur, the
+// returned signal will error out.
++ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.m
new file mode 100644
index 0000000..3eaa2c3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.m
@@ -0,0 +1,53 @@
+//
+// NSURLConnection+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSURLConnection+RACSupport.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACSubscriber.h"
+#import "RACTuple.h"
+
+@implementation NSURLConnection (RACSupport)
+
++ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request {
+ NSCParameterAssert(request != nil);
+
+ return [[RACSignal
+ createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ queue.name = @"com.github.ReactiveCocoa.NSURLConnectionRACSupport";
+
+ [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
+ // The docs say that `nil` data means an error occurred, but
+ // `nil` responses can also occur in practice (circumstances
+ // unknown). Consider either to be an error.
+ //
+ // Note that _empty_ data is not necessarily erroneous, as there
+ // may be headers but no HTTP body.
+ if (response == nil || data == nil) {
+ [subscriber sendError:error];
+ } else {
+ [subscriber sendNext:RACTuplePack(response, data)];
+ [subscriber sendCompleted];
+ }
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ // It's not clear if this will actually cancel the connection,
+ // but we can at least prevent _some_ unnecessary work --
+ // without writing all the code for a proper delegate, which
+ // doesn't really belong in RAC.
+ queue.suspended = YES;
+ [queue cancelAllOperations];
+ }];
+ }]
+ setNameWithFormat:@"+rac_sendAsynchronousRequest: %@", request];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.h
new file mode 100644
index 0000000..8482fb3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.h
@@ -0,0 +1,27 @@
+//
+// NSUserDefaults+RACSupport.h
+// ReactiveCocoa
+//
+// Created by Matt Diephouse on 12/19/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACChannelTerminal;
+
+@interface NSUserDefaults (RACSupport)
+
+/// Creates and returns a terminal for binding the user defaults key.
+///
+/// **Note:** The value in the user defaults is *asynchronously* updated with
+/// values sent to the channel.
+///
+/// key - The user defaults key to create the channel terminal for.
+///
+/// Returns a channel terminal that sends the value of the user defaults key
+/// upon subscription, sends an updated value whenever the default changes, and
+/// updates the default asynchronously with values it receives.
+- (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.m
new file mode 100644
index 0000000..a8624a1
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/NSUserDefaults+RACSupport.m
@@ -0,0 +1,58 @@
+//
+// NSUserDefaults+RACSupport.m
+// ReactiveCocoa
+//
+// Created by Matt Diephouse on 12/19/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSUserDefaults+RACSupport.h"
+
+#import "EXTScope.h"
+#import "RACChannel.h"
+#import "RACScheduler.h"
+#import "RACSignal+Operations.h"
+#import "NSNotificationCenter+RACSupport.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACLifting.h"
+
+@implementation NSUserDefaults (RACSupport)
+
+- (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key {
+ RACChannel *channel = [RACChannel new];
+
+ RACScheduler *scheduler = [RACScheduler scheduler];
+ __block BOOL ignoreNextValue = NO;
+
+ @weakify(self);
+ [[[[[[[NSNotificationCenter.defaultCenter
+ rac_addObserverForName:NSUserDefaultsDidChangeNotification object:self]
+ map:^(id _) {
+ @strongify(self);
+ return [self objectForKey:key];
+ }]
+ startWith:[self objectForKey:key]]
+ // Don't send values that were set on the other side of the terminal.
+ filter:^ BOOL (id _) {
+ if (RACScheduler.currentScheduler == scheduler && ignoreNextValue) {
+ ignoreNextValue = NO;
+ return NO;
+ }
+ return YES;
+ }]
+ distinctUntilChanged]
+ takeUntil:self.rac_willDeallocSignal]
+ subscribe:channel.leadingTerminal];
+
+ [[channel.leadingTerminal
+ deliverOn:scheduler]
+ subscribeNext:^(id value) {
+ @strongify(self);
+ ignoreNextValue = YES;
+ [self setObject:value forKey:key];
+ }];
+
+ return channel.followingTerminal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.h
new file mode 100644
index 0000000..e9d948e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.h
@@ -0,0 +1,18 @@
+//
+// RACArraySequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class that adapts an array to the RACSequence interface.
+@interface RACArraySequence : RACSequence
+
+// Returns a sequence for enumerating over the given array, starting from the
+// given offset. The array will be copied to prevent mutation.
++ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.m
new file mode 100644
index 0000000..f7ca69d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.m
@@ -0,0 +1,125 @@
+//
+// RACArraySequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACArraySequence.h"
+
+@interface RACArraySequence ()
+
+// Redeclared from the superclass and marked deprecated to prevent using `array`
+// where `backingArray` is intended.
+@property (nonatomic, copy, readonly) NSArray *array __attribute__((deprecated));
+
+// The array being sequenced.
+@property (nonatomic, copy, readonly) NSArray *backingArray;
+
+// The index in the array from which the sequence starts.
+@property (nonatomic, assign, readonly) NSUInteger offset;
+
+@end
+
+@implementation RACArraySequence
+
+#pragma mark Lifecycle
+
++ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset {
+ NSCParameterAssert(offset <= array.count);
+
+ if (offset == array.count) return self.empty;
+
+ RACArraySequence *seq = [[self alloc] init];
+ seq->_backingArray = [array copy];
+ seq->_offset = offset;
+ return seq;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ return [self.backingArray objectAtIndex:self.offset];
+}
+
+- (RACSequence *)tail {
+ RACSequence *sequence = [self.class sequenceWithArray:self.backingArray offset:self.offset + 1];
+ sequence.name = self.name;
+ return sequence;
+}
+
+#pragma mark NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id[])stackbuf count:(NSUInteger)len {
+ NSCParameterAssert(len > 0);
+
+ if (state->state >= self.backingArray.count) {
+ // Enumeration has completed.
+ return 0;
+ }
+
+ if (state->state == 0) {
+ state->state = self.offset;
+
+ // Since a sequence doesn't mutate, this just needs to be set to
+ // something non-NULL.
+ state->mutationsPtr = state->extra;
+ }
+
+ state->itemsPtr = stackbuf;
+
+ NSUInteger startIndex = state->state;
+ NSUInteger index = 0;
+
+ for (id value in self.backingArray) {
+ // Constructing an index set for -enumerateObjectsAtIndexes: can actually be
+ // slower than just skipping the items we don't care about.
+ if (index < startIndex) {
+ ++index;
+ continue;
+ }
+
+ stackbuf[index - startIndex] = value;
+
+ ++index;
+ if (index - startIndex >= len) break;
+ }
+
+ NSCAssert(index > startIndex, @"Final index (%lu) should be greater than start index (%lu)", (unsigned long)index, (unsigned long)startIndex);
+
+ state->state = index;
+ return index - startIndex;
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+- (NSArray *)array {
+ return [self.backingArray subarrayWithRange:NSMakeRange(self.offset, self.backingArray.count - self.offset)];
+}
+#pragma clang diagnostic pop
+
+#pragma mark NSCoding
+
+- (id)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+ if (self == nil) return nil;
+
+ _backingArray = [coder decodeObjectForKey:@"array"];
+ _offset = 0;
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ // Encoding is handled in RACSequence.
+ [super encodeWithCoder:coder];
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, array = %@ }", self.class, self, self.name, self.backingArray];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.h b/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.h
new file mode 100644
index 0000000..8588c80
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.h
@@ -0,0 +1,70 @@
+//
+// RACBacktrace.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-08-20.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#ifdef DEBUG
+
+extern void rac_dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
+extern void rac_dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
+extern void rac_dispatch_after(dispatch_time_t time, dispatch_queue_t queue, dispatch_block_t block);
+extern void rac_dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function);
+extern void rac_dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function);
+extern void rac_dispatch_after_f(dispatch_time_t time, dispatch_queue_t queue, void *context, dispatch_function_t function);
+
+#define dispatch_async rac_dispatch_async
+#define dispatch_barrier_async rac_dispatch_barrier_async
+#define dispatch_after rac_dispatch_after
+#define dispatch_async_f rac_dispatch_async_f
+#define dispatch_barrier_async_f rac_dispatch_barrier_async_f
+#define dispatch_after_f rac_dispatch_after_f
+
+/// Preserves backtraces across asynchronous calls.
+///
+/// On OS X, you can enable the automatic capturing of asynchronous backtraces
+/// (in Debug builds) by setting the `DYLD_INSERT_LIBRARIES` environment variable
+/// to `@executable_path/../Frameworks/ReactiveCocoa.framework/ReactiveCocoa` in
+/// your scheme's Run action settings.
+///
+/// On iOS, your project and RAC will automatically use the `rac_` GCD functions
+/// (declared above) for asynchronous work. Unfortunately, unlike OS X, it's
+/// impossible to capture backtraces inside NSOperationQueue or other code
+/// outside of your project.
+///
+/// Once backtraces are being captured, you can `po [RACBacktrace backtrace]` in
+/// the debugger to print them out at any time. You can even set up an alias in
+/// ~/.lldbinit to do so:
+///
+/// command alias racbt po [RACBacktrace backtrace]
+///
+@interface RACBacktrace : NSObject
+
+/// The backtrace from any previous thread.
+@property (nonatomic, strong, readonly) RACBacktrace *previousThreadBacktrace;
+
+/// The call stack of this backtrace's thread.
+@property (nonatomic, copy, readonly) NSArray *callStackSymbols;
+
+/// Captures the current thread's backtrace, appending it to any backtrace from
+/// a previous thread.
++ (instancetype)backtrace;
+
+/// Same as +backtrace, but omits the specified number of frames at the
+/// top of the stack (in addition to this method itself).
++ (instancetype)backtraceIgnoringFrames:(NSUInteger)ignoreCount;
+
+@end
+
+#else
+
+#define rac_dispatch_async dispatch_async
+#define rac_dispatch_barrier_async dispatch_barrier_async
+#define rac_dispatch_after dispatch_after
+#define rac_dispatch_async_f dispatch_async_f
+#define rac_dispatch_barrier_async_f dispatch_barrier_async_f
+#define rac_dispatch_after_f dispatch_after_f
+
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.m b/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.m
new file mode 100644
index 0000000..c2724d4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.m
@@ -0,0 +1,249 @@
+//
+// RACBacktrace.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-08-16.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <execinfo.h>
+#import <pthread.h>
+#import "RACBacktrace.h"
+
+#define RAC_BACKTRACE_MAX_CALL_STACK_FRAMES 128
+
+#ifdef DEBUG
+
+// Undefine the macros that hide the real GCD functions.
+#undef dispatch_async
+#undef dispatch_barrier_async
+#undef dispatch_after
+#undef dispatch_async_f
+#undef dispatch_barrier_async_f
+#undef dispatch_after_f
+
+@interface RACBacktrace () {
+ void *_callStackAddresses[RAC_BACKTRACE_MAX_CALL_STACK_FRAMES];
+ int _callStackSize;
+}
+
+@property (nonatomic, strong, readwrite) RACBacktrace *previousThreadBacktrace;
+@end
+
+@interface RACDispatchInfo : NSObject
+
+// The recorded backtrace.
+@property (nonatomic, strong, readonly) RACBacktrace *backtrace;
+
+// The information for the original dispatch.
+@property (nonatomic, readonly) dispatch_function_t function;
+@property (nonatomic, readonly) void *context;
+@property (nonatomic, readonly) dispatch_queue_t queue;
+
+- (id)initWithQueue:(dispatch_queue_t)queue function:(dispatch_function_t)function context:(void *)context;
+
+@end
+
+// Function for use with dispatch_async_f and friends, which will save the
+// backtrace onto the current queue, then call through to the original dispatch.
+static void RACTraceDispatch (void *ptr) {
+ // Balance out the retain necessary for async calls.
+ RACDispatchInfo *info __attribute__((objc_precise_lifetime)) = CFBridgingRelease(ptr);
+
+ dispatch_queue_set_specific(info.queue, (void *)pthread_self(), (__bridge void *)info.backtrace, NULL);
+ info.function(info.context);
+ dispatch_queue_set_specific(info.queue, (void *)pthread_self(), NULL, NULL);
+}
+
+// Always inline this function, for consistency in backtraces.
+__attribute__((always_inline))
+static dispatch_block_t RACBacktraceBlock (dispatch_queue_t queue, dispatch_block_t block) {
+ RACBacktrace *backtrace = [RACBacktrace backtrace];
+
+ return [^{
+ RACBacktrace *backtraceKeptAlive __attribute__((objc_precise_lifetime)) = backtrace;
+
+ dispatch_queue_set_specific(queue, (void *)pthread_self(), (__bridge void *)backtraceKeptAlive, NULL);
+ block();
+ dispatch_queue_set_specific(queue, (void *)pthread_self(), NULL, NULL);
+ } copy];
+}
+
+void rac_dispatch_async(dispatch_queue_t queue, dispatch_block_t block) {
+ dispatch_async(queue, RACBacktraceBlock(queue, block));
+}
+
+void rac_dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block) {
+ dispatch_barrier_async(queue, RACBacktraceBlock(queue, block));
+}
+
+void rac_dispatch_after(dispatch_time_t time, dispatch_queue_t queue, dispatch_block_t block) {
+ dispatch_after(time, queue, RACBacktraceBlock(queue, block));
+}
+
+void rac_dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function) {
+ RACDispatchInfo *info = [[RACDispatchInfo alloc] initWithQueue:queue function:function context:context];
+ dispatch_async_f(queue, (void *)CFBridgingRetain(info), &RACTraceDispatch);
+}
+
+void rac_dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function) {
+ RACDispatchInfo *info = [[RACDispatchInfo alloc] initWithQueue:queue function:function context:context];
+ dispatch_barrier_async_f(queue, (void *)CFBridgingRetain(info), &RACTraceDispatch);
+}
+
+void rac_dispatch_after_f(dispatch_time_t time, dispatch_queue_t queue, void *context, dispatch_function_t function) {
+ RACDispatchInfo *info = [[RACDispatchInfo alloc] initWithQueue:queue function:function context:context];
+ dispatch_after_f(time, queue, (void *)CFBridgingRetain(info), &RACTraceDispatch);
+}
+
+// This is what actually performs the injection.
+//
+// The DYLD_INSERT_LIBRARIES environment variable must include the RAC dynamic
+// library in order for this to work.
+__attribute__((used)) static struct { const void *replacement; const void *replacee; } interposers[] __attribute__((section("__DATA,__interpose"))) = {
+ { (const void *)&rac_dispatch_async, (const void *)&dispatch_async },
+ { (const void *)&rac_dispatch_barrier_async, (const void *)&dispatch_barrier_async },
+ { (const void *)&rac_dispatch_after, (const void *)&dispatch_after },
+ { (const void *)&rac_dispatch_async_f, (const void *)&dispatch_async_f },
+ { (const void *)&rac_dispatch_barrier_async_f, (const void *)&dispatch_barrier_async_f },
+ { (const void *)&rac_dispatch_after_f, (const void *)&dispatch_after_f },
+};
+
+static void RACSignalHandler (int sig) {
+ NSLog(@"Backtrace: %@", [RACBacktrace backtrace]);
+ fflush(stdout);
+
+ // Restore the default action and raise the signal again.
+ signal(sig, SIG_DFL);
+ raise(sig);
+}
+
+static void RACExceptionHandler (NSException *ex) {
+ NSLog(@"Uncaught exception %@", ex);
+ NSLog(@"Backtrace: %@", [RACBacktrace backtrace]);
+ fflush(stdout);
+}
+
+@implementation RACBacktrace
+
+#pragma mark Properties
+
+- (NSArray *)callStackSymbols {
+ if (_callStackSize == 0) return @[];
+
+ char **symbols = backtrace_symbols(_callStackAddresses, _callStackSize);
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:(NSUInteger)_callStackSize];
+
+ for (int i = 0; i < _callStackSize; i++) {
+ NSString *str = @(symbols[i]);
+ [array addObject:str];
+ }
+
+ free(symbols);
+ return array;
+}
+
+#pragma mark Lifecycle
+
++ (void)load {
+ @autoreleasepool {
+ NSString *libraries = [[[NSProcessInfo processInfo] environment] objectForKey:@"DYLD_INSERT_LIBRARIES"];
+
+ // Don't install our handlers if we're not actually intercepting function
+ // calls.
+ if ([libraries rangeOfString:@"ReactiveCocoa"].length == 0) return;
+
+ NSLog(@"*** Enabling asynchronous backtraces");
+
+ NSSetUncaughtExceptionHandler(&RACExceptionHandler);
+ }
+
+ signal(SIGILL, &RACSignalHandler);
+ signal(SIGTRAP, &RACSignalHandler);
+ signal(SIGABRT, &RACSignalHandler);
+ signal(SIGFPE, &RACSignalHandler);
+ signal(SIGBUS, &RACSignalHandler);
+ signal(SIGSEGV, &RACSignalHandler);
+ signal(SIGSYS, &RACSignalHandler);
+ signal(SIGPIPE, &RACSignalHandler);
+}
+
+- (void)dealloc {
+ __autoreleasing RACBacktrace *previous __attribute__((unused)) = self.previousThreadBacktrace;
+ self.previousThreadBacktrace = nil;
+}
+
+#pragma mark Backtraces
+
++ (instancetype)backtrace {
+ return [self backtraceIgnoringFrames:1];
+}
+
++ (instancetype)backtraceIgnoringFrames:(NSUInteger)ignoreCount {
+ @autoreleasepool {
+ RACBacktrace *oldBacktrace = (__bridge id)dispatch_get_specific((void *)pthread_self());
+
+ RACBacktrace *newBacktrace = [[RACBacktrace alloc] init];
+ newBacktrace.previousThreadBacktrace = oldBacktrace;
+
+ int size = backtrace(newBacktrace->_callStackAddresses, RAC_BACKTRACE_MAX_CALL_STACK_FRAMES);
+
+ // Omit this method plus however many others from the backtrace.
+ ++ignoreCount;
+ if ((NSUInteger)size > ignoreCount) {
+ memmove(newBacktrace->_callStackAddresses, newBacktrace->_callStackAddresses + ignoreCount, ((NSUInteger)size - ignoreCount) * sizeof(char *));
+ size -= (int)ignoreCount;
+ }
+
+ newBacktrace->_callStackSize = size;
+ return newBacktrace;
+ }
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ NSString *str = [NSString stringWithFormat:@"%@", self.callStackSymbols];
+ if (self.previousThreadBacktrace != nil) {
+ str = [str stringByAppendingFormat:@"\n\n... asynchronously invoked from: %@", self.previousThreadBacktrace];
+ }
+
+ return str;
+}
+
+@end
+
+@implementation RACDispatchInfo
+
+#pragma mark Lifecycle
+
+- (id)initWithQueue:(dispatch_queue_t)queue function:(dispatch_function_t)function context:(void *)context {
+ @autoreleasepool {
+ NSCParameterAssert(queue != NULL);
+ NSCParameterAssert(function != NULL);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _backtrace = [RACBacktrace backtraceIgnoringFrames:1];
+
+ dispatch_retain(queue);
+ _queue = queue;
+
+ _function = function;
+ _context = context;
+
+ return self;
+ }
+}
+
+- (void)dealloc {
+ if (_queue != NULL) {
+ dispatch_release(_queue);
+ _queue = NULL;
+ }
+}
+
+@end
+
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.h b/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.h
new file mode 100644
index 0000000..2f9e0db
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.h
@@ -0,0 +1,19 @@
+//
+// RACBehaviorSubject.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubject.h"
+
+
+/// A behavior subject sends the last value it received when it is subscribed to.
+@interface RACBehaviorSubject : RACSubject
+
+/// Creates a new behavior subject with a default value. If it hasn't received
+/// any values when it gets subscribed to, it sends the default value.
++ (instancetype)behaviorSubjectWithDefaultValue:(id)value;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.m b/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.m
new file mode 100644
index 0000000..dfda2ac
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.m
@@ -0,0 +1,56 @@
+//
+// RACBehaviorSubject.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACBehaviorSubject.h"
+#import "RACDisposable.h"
+#import "RACScheduler+Private.h"
+
+@interface RACBehaviorSubject ()
+
+// This property should only be used while synchronized on self.
+@property (nonatomic, strong) id currentValue;
+
+@end
+
+@implementation RACBehaviorSubject
+
+#pragma mark Lifecycle
+
++ (instancetype)behaviorSubjectWithDefaultValue:(id)value {
+ RACBehaviorSubject *subject = [self subject];
+ subject.currentValue = value;
+ return subject;
+}
+
+#pragma mark RACSignal
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
+
+ RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
+ @synchronized (self) {
+ [subscriber sendNext:self.currentValue];
+ }
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [subscriptionDisposable dispose];
+ [schedulingDisposable dispose];
+ }];
+}
+
+#pragma mark RACSubscriber
+
+- (void)sendNext:(id)value {
+ @synchronized (self) {
+ self.currentValue = value;
+ [super sendNext:value];
+ }
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.h b/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.h
new file mode 100644
index 0000000..3857d7c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.h
@@ -0,0 +1,30 @@
+//
+// RACBlockTrampoline.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 10/21/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACTuple;
+
+// A private class that allows a limited type of dynamic block invocation.
+@interface RACBlockTrampoline : NSObject
+
+// Invokes the given block with the given arguments. All of the block's
+// argument types must be objects and it must be typed to return an object.
+//
+// At this time, it only supports blocks that take up to 15 arguments. Any more
+// is just cray.
+//
+// block - The block to invoke. Must accept as many arguments as are given in
+// the arguments array. Cannot be nil.
+// arguments - The arguments with which to invoke the block. `RACTupleNil`s will
+// be passed as nils.
+//
+// Returns the return value of invoking the block.
++ (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.m b/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.m
new file mode 100644
index 0000000..07903dd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.m
@@ -0,0 +1,156 @@
+//
+// RACBlockTrampoline.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 10/21/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACBlockTrampoline.h"
+#import "RACTuple.h"
+
+@interface RACBlockTrampoline ()
+@property (nonatomic, readonly, copy) id block;
+@end
+
+@implementation RACBlockTrampoline
+
+#pragma mark API
+
+- (id)initWithBlock:(id)block {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _block = [block copy];
+
+ return self;
+}
+
++ (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments {
+ NSCParameterAssert(block != NULL);
+
+ RACBlockTrampoline *trampoline = [[self alloc] initWithBlock:block];
+ return [trampoline invokeWithArguments:arguments];
+}
+
+- (id)invokeWithArguments:(RACTuple *)arguments {
+ SEL selector = [self selectorForArgumentCount:arguments.count];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
+ invocation.selector = selector;
+ invocation.target = self;
+
+ for (NSUInteger i = 0; i < arguments.count; i++) {
+ id arg = arguments[i];
+ NSInteger argIndex = (NSInteger)(i + 2);
+ [invocation setArgument:&arg atIndex:argIndex];
+ }
+
+ [invocation invoke];
+
+ __unsafe_unretained id returnVal;
+ [invocation getReturnValue:&returnVal];
+ return returnVal;
+}
+
+- (SEL)selectorForArgumentCount:(NSUInteger)count {
+ NSCParameterAssert(count > 0);
+
+ switch (count) {
+ case 0: return NULL;
+ case 1: return @selector(performWith:);
+ case 2: return @selector(performWith::);
+ case 3: return @selector(performWith:::);
+ case 4: return @selector(performWith::::);
+ case 5: return @selector(performWith:::::);
+ case 6: return @selector(performWith::::::);
+ case 7: return @selector(performWith:::::::);
+ case 8: return @selector(performWith::::::::);
+ case 9: return @selector(performWith:::::::::);
+ case 10: return @selector(performWith::::::::::);
+ case 11: return @selector(performWith:::::::::::);
+ case 12: return @selector(performWith::::::::::::);
+ case 13: return @selector(performWith:::::::::::::);
+ case 14: return @selector(performWith::::::::::::::);
+ case 15: return @selector(performWith:::::::::::::::);
+ }
+
+ NSCAssert(NO, @"The argument count is too damn high! Only blocks of up to 15 arguments are currently supported.");
+ return NULL;
+}
+
+- (id)performWith:(id)obj1 {
+ id (^block)(id) = self.block;
+ return block(obj1);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 {
+ id (^block)(id, id) = self.block;
+ return block(obj1, obj2);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 {
+ id (^block)(id, id, id) = self.block;
+ return block(obj1, obj2, obj3);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 {
+ id (^block)(id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 {
+ id (^block)(id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 {
+ id (^block)(id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 {
+ id (^block)(id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 {
+ id (^block)(id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 {
+ id (^block)(id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 :(id)obj14 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13, obj14);
+}
+
+- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 :(id)obj14 :(id)obj15 {
+ id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block;
+ return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13, obj14, obj15);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.h b/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.h
new file mode 100644
index 0000000..ef1d6f1
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.h
@@ -0,0 +1,70 @@
+//
+// RACChannel.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 01/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+
+@class RACChannelTerminal;
+
+/// A two-way channel.
+///
+/// Conceptually, RACChannel can be thought of as a bidirectional connection,
+/// composed of two controllable signals that work in parallel.
+///
+/// For example, when connecting between a view and a model:
+///
+/// Model View
+/// `leadingTerminal` ------> `followingTerminal`
+/// `leadingTerminal` <------ `followingTerminal`
+///
+/// The initial value of the model and all future changes to it are _sent on_ the
+/// `leadingTerminal`, and _received by_ subscribers of the `followingTerminal`.
+///
+/// Likewise, whenever the user changes the value of the view, that value is sent
+/// on the `followingTerminal`, and received in the model from the
+/// `leadingTerminal`. However, the initial value of the view is not received
+/// from the `leadingTerminal` (only future changes).
+@interface RACChannel : NSObject
+
+/// The terminal which "leads" the channel, by sending its latest value
+/// immediately to new subscribers of the `followingTerminal`.
+///
+/// New subscribers to this terminal will not receive a starting value, but will
+/// receive all future values that are sent to the `followingTerminal`.
+@property (nonatomic, strong, readonly) RACChannelTerminal *leadingTerminal;
+
+/// The terminal which "follows" the lead of the other terminal, only sending
+/// _future_ values to the subscribers of the `leadingTerminal`.
+///
+/// The latest value sent to the `leadingTerminal` (if any) will be sent
+/// immediately to new subscribers of this terminal, and then all future values
+/// as well.
+@property (nonatomic, strong, readonly) RACChannelTerminal *followingTerminal;
+
+@end
+
+/// Represents one end of a RACChannel.
+///
+/// An terminal is similar to a socket or pipe -- it represents one end of
+/// a connection (the RACChannel, in this case). Values sent to this terminal
+/// will _not_ be received by its subscribers. Instead, the values will be sent
+/// to the subscribers of the RACChannel's _other_ terminal.
+///
+/// For example, when using the `followingTerminal`, _sent_ values can only be
+/// _received_ from the `leadingTerminal`, and vice versa.
+///
+/// To make it easy to terminate a RACChannel, `error` and `completed` events
+/// sent to either terminal will be received by the subscribers of _both_
+/// terminals.
+///
+/// Do not instantiate this class directly. Create a RACChannel instead.
+@interface RACChannelTerminal : RACSignal <RACSubscriber>
+
+- (id)init __attribute__((unavailable("Instantiate a RACChannel instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.m b/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.m
new file mode 100644
index 0000000..7222858
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACChannel.m
@@ -0,0 +1,91 @@
+//
+// RACChannel.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 01/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACChannel.h"
+#import "RACDisposable.h"
+#import "RACReplaySubject.h"
+#import "RACSignal+Operations.h"
+#import "RACUnit.h"
+
+@interface RACChannelTerminal ()
+
+// The values for this terminal.
+@property (nonatomic, strong, readonly) RACSignal *values;
+
+// A subscriber will will send values to the other terminal.
+@property (nonatomic, strong, readonly) id<RACSubscriber> otherTerminal;
+
+- (id)initWithValues:(RACSignal *)values otherTerminal:(id<RACSubscriber>)otherTerminal;
+
+@end
+
+@implementation RACChannel
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ // We don't want any starting value from the leadingSubject, but we do want
+ // error and completion to be replayed.
+ RACReplaySubject *leadingSubject = [[RACReplaySubject replaySubjectWithCapacity:0] setNameWithFormat:@"leadingSubject"];
+ RACReplaySubject *followingSubject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"followingSubject"];
+
+ // Propagate errors and completion to everything.
+ [[leadingSubject ignoreValues] subscribe:followingSubject];
+ [[followingSubject ignoreValues] subscribe:leadingSubject];
+
+ _leadingTerminal = [[[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject] setNameWithFormat:@"leadingTerminal"];
+ _followingTerminal = [[[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject] setNameWithFormat:@"followingTerminal"];
+
+ return self;
+}
+
+@end
+
+@implementation RACChannelTerminal
+
+#pragma mark Lifecycle
+
+- (id)initWithValues:(RACSignal *)values otherTerminal:(id<RACSubscriber>)otherTerminal {
+ NSCParameterAssert(values != nil);
+ NSCParameterAssert(otherTerminal != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _values = values;
+ _otherTerminal = otherTerminal;
+
+ return self;
+}
+
+#pragma mark RACSignal
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ return [self.values subscribe:subscriber];
+}
+
+#pragma mark <RACSubscriber>
+
+- (void)sendNext:(id)value {
+ [self.otherTerminal sendNext:value];
+}
+
+- (void)sendError:(NSError *)error {
+ [self.otherTerminal sendError:error];
+}
+
+- (void)sendCompleted {
+ [self.otherTerminal sendCompleted];
+}
+
+- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable {
+ [self.otherTerminal didSubscribeWithDisposable:disposable];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h b/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h
new file mode 100644
index 0000000..653d46b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h
@@ -0,0 +1,123 @@
+//
+// RACCommand.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/3/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+/// The domain for errors originating within `RACCommand`.
+extern NSString * const RACCommandErrorDomain;
+
+/// -execute: was invoked while the command was disabled.
+extern const NSInteger RACCommandErrorNotEnabled;
+
+/// A `userInfo` key for an error, associated with the `RACCommand` that the
+/// error originated from.
+///
+/// This is included only when the error code is `RACCommandErrorNotEnabled`.
+extern NSString * const RACUnderlyingCommandErrorKey;
+
+/// A command is a signal triggered in response to some action, typically
+/// UI-related.
+@interface RACCommand : NSObject
+
+/// A signal of the signals returned by successful invocations of -execute:
+/// (i.e., while the receiver is `enabled`).
+///
+/// Errors will be automatically caught upon the inner signals, and sent upon
+/// `errors` instead. If you _want_ to receive inner errors, use -execute: or
+/// -[RACSignal materialize].
+///
+/// Only executions that begin _after_ subscription will be sent upon this
+/// signal. All inner signals will arrive upon the main thread.
+@property (nonatomic, strong, readonly) RACSignal *executionSignals;
+
+/// A signal of whether this command is currently executing.
+///
+/// This will send YES whenever -execute: is invoked and the created signal has
+/// not yet terminated. Once all executions have terminated, `executing` will
+/// send NO.
+///
+/// This signal will send its current value upon subscription, and then all
+/// future values on the main thread.
+@property (nonatomic, strong, readonly) RACSignal *executing;
+
+/// A signal of whether this command is able to execute.
+///
+/// This will send NO if:
+///
+/// - The command was created with an `enabledSignal`, and NO is sent upon that
+/// signal, or
+/// - `allowsConcurrentExecution` is NO and the command has started executing.
+///
+/// Once the above conditions are no longer met, the signal will send YES.
+///
+/// This signal will send its current value upon subscription, and then all
+/// future values on the main thread.
+@property (nonatomic, strong, readonly) RACSignal *enabled;
+
+/// Forwards any errors that occur within signals returned by -execute:.
+///
+/// When an error occurs on a signal returned from -execute:, this signal will
+/// send the associated NSError value as a `next` event (since an `error` event
+/// would terminate the stream).
+///
+/// After subscription, this signal will send all future errors on the main
+/// thread.
+@property (nonatomic, strong, readonly) RACSignal *errors;
+
+/// Whether the command allows multiple executions to proceed concurrently.
+///
+/// The default value for this property is NO.
+@property (atomic, assign) BOOL allowsConcurrentExecution;
+
+/// Invokes -initWithEnabled:signalBlock: with a nil `enabledSignal`.
+- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock;
+
+/// Initializes a command that is conditionally enabled.
+///
+/// This is the designated initializer for this class.
+///
+/// enabledSignal - A signal of BOOLs which indicate whether the command should
+/// be enabled. `enabled` will be based on the latest value sent
+/// from this signal. Before any values are sent, `enabled` will
+/// default to YES. This argument may be nil.
+/// signalBlock - A block which will map each input value (passed to -execute:)
+/// to a signal of work. The returned signal will be multicasted
+/// to a replay subject, sent on `executionSignals`, then
+/// subscribed to synchronously. Neither the block nor the
+/// returned signal may be nil.
+- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock;
+
+/// If the receiver is enabled, this method will:
+///
+/// 1. Invoke the `signalBlock` given at the time of initialization.
+/// 2. Multicast the returned signal to a RACReplaySubject.
+/// 3. Send the multicasted signal on `executionSignals`.
+/// 4. Subscribe (connect) to the original signal on the main thread.
+///
+/// input - The input value to pass to the receiver's `signalBlock`. This may be
+/// nil.
+///
+/// Returns the multicasted signal, after subscription. If the receiver is not
+/// enabled, returns a signal that will send an error with code
+/// RACCommandErrorNotEnabled.
+- (RACSignal *)execute:(id)input;
+
+@end
+
+@interface RACCommand (Unavailable)
+
+@property (atomic, readonly) BOOL canExecute __attribute__((unavailable("Use the 'enabled' signal instead")));
+
++ (instancetype)command __attribute__((unavailable("Use -initWithSignalBlock: instead")));
++ (instancetype)commandWithCanExecuteSignal:(RACSignal *)canExecuteSignal __attribute__((unavailable("Use -initWithEnabled:signalBlock: instead")));
+- (id)initWithCanExecuteSignal:(RACSignal *)canExecuteSignal __attribute__((unavailable("Use -initWithEnabled:signalBlock: instead")));
+- (RACSignal *)addSignalBlock:(RACSignal * (^)(id value))signalBlock __attribute__((unavailable("Pass the signalBlock to -initWithSignalBlock: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.m b/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.m
new file mode 100644
index 0000000..0bc864b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.m
@@ -0,0 +1,268 @@
+//
+// RACCommand.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/3/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACCommand.h"
+#import "EXTScope.h"
+#import "NSArray+RACSequenceAdditions.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACMulticastConnection.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import "RACSequence.h"
+#import "RACSerialDisposable.h"
+#import "RACSignal+Operations.h"
+#import <libkern/OSAtomic.h>
+
+NSString * const RACCommandErrorDomain = @"RACCommandErrorDomain";
+NSString * const RACUnderlyingCommandErrorKey = @"RACUnderlyingCommandErrorKey";
+
+const NSInteger RACCommandErrorNotEnabled = 1;
+
+@interface RACCommand () {
+ // The mutable array backing `activeExecutionSignals`.
+ //
+ // This should only be used while synchronized on `self`.
+ NSMutableArray *_activeExecutionSignals;
+
+ // Atomic backing variable for `allowsConcurrentExecution`.
+ volatile uint32_t _allowsConcurrentExecution;
+}
+
+// An array of signals representing in-flight executions, in the order they
+// began.
+//
+// This property is KVO-compliant.
+@property (atomic, copy, readonly) NSArray *activeExecutionSignals;
+
+// `enabled`, but without a hop to the main thread.
+//
+// Values from this signal may arrive on any thread.
+@property (nonatomic, strong, readonly) RACSignal *immediateEnabled;
+
+// The signal block that the receiver was initialized with.
+@property (nonatomic, copy, readonly) RACSignal * (^signalBlock)(id input);
+
+// Adds a signal to `activeExecutionSignals` and generates a KVO notification.
+- (void)addActiveExecutionSignal:(RACSignal *)signal;
+
+// Removes a signal from `activeExecutionSignals` and generates a KVO
+// notification.
+- (void)removeActiveExecutionSignal:(RACSignal *)signal;
+
+@end
+
+@implementation RACCommand
+
+#pragma mark Properties
+
+- (BOOL)allowsConcurrentExecution {
+ return _allowsConcurrentExecution != 0;
+}
+
+- (void)setAllowsConcurrentExecution:(BOOL)allowed {
+ [self willChangeValueForKey:@keypath(self.allowsConcurrentExecution)];
+
+ if (allowed) {
+ OSAtomicOr32Barrier(1, &_allowsConcurrentExecution);
+ } else {
+ OSAtomicAnd32Barrier(0, &_allowsConcurrentExecution);
+ }
+
+ [self didChangeValueForKey:@keypath(self.allowsConcurrentExecution)];
+}
+
+- (NSArray *)activeExecutionSignals {
+ @synchronized (self) {
+ return [_activeExecutionSignals copy];
+ }
+}
+
+- (void)addActiveExecutionSignal:(RACSignal *)signal {
+ NSCParameterAssert([signal isKindOfClass:RACSignal.class]);
+
+ @synchronized (self) {
+ // The KVO notification has to be generated while synchronized, because
+ // it depends on the index remaining consistent.
+ NSIndexSet *indexes = [NSIndexSet indexSetWithIndex:_activeExecutionSignals.count];
+ [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@keypath(self.activeExecutionSignals)];
+ [_activeExecutionSignals addObject:signal];
+ [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@keypath(self.activeExecutionSignals)];
+ }
+}
+
+- (void)removeActiveExecutionSignal:(RACSignal *)signal {
+ NSCParameterAssert([signal isKindOfClass:RACSignal.class]);
+
+ @synchronized (self) {
+ // The indexes have to be calculated and the notification generated
+ // while synchronized, because they depend on the indexes remaining
+ // consistent.
+ NSIndexSet *indexes = [_activeExecutionSignals indexesOfObjectsPassingTest:^ BOOL (RACSignal *obj, NSUInteger index, BOOL *stop) {
+ return obj == signal;
+ }];
+
+ if (indexes.count == 0) return;
+
+ [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@keypath(self.activeExecutionSignals)];
+ [_activeExecutionSignals removeObjectsAtIndexes:indexes];
+ [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@keypath(self.activeExecutionSignals)];
+ }
+}
+
+#pragma mark Lifecycle
+
+- (id)init {
+ NSCAssert(NO, @"Use -initWithSignalBlock: instead");
+ return nil;
+}
+
+- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock {
+ return [self initWithEnabled:nil signalBlock:signalBlock];
+}
+
+- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock {
+ NSCParameterAssert(signalBlock != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _activeExecutionSignals = [[NSMutableArray alloc] init];
+ _signalBlock = [signalBlock copy];
+
+ // A signal of additions to `activeExecutionSignals`.
+ RACSignal *newActiveExecutionSignals = [[[[[self
+ rac_valuesAndChangesForKeyPath:@keypath(self.activeExecutionSignals) options:NSKeyValueObservingOptionNew observer:nil]
+ reduceEach:^(id _, NSDictionary *change) {
+ NSArray *signals = change[NSKeyValueChangeNewKey];
+ if (signals == nil) return [RACSignal empty];
+
+ return [signals.rac_sequence signalWithScheduler:RACScheduler.immediateScheduler];
+ }]
+ concat]
+ publish]
+ autoconnect];
+
+ _executionSignals = [[[newActiveExecutionSignals
+ map:^(RACSignal *signal) {
+ return [signal catchTo:[RACSignal empty]];
+ }]
+ deliverOn:RACScheduler.mainThreadScheduler]
+ setNameWithFormat:@"%@ -executionSignals", self];
+
+ // `errors` needs to be multicasted so that it picks up all
+ // `activeExecutionSignals` that are added.
+ //
+ // In other words, if someone subscribes to `errors` _after_ an execution
+ // has started, it should still receive any error from that execution.
+ RACMulticastConnection *errorsConnection = [[[newActiveExecutionSignals
+ flattenMap:^(RACSignal *signal) {
+ return [[signal
+ ignoreValues]
+ catch:^(NSError *error) {
+ return [RACSignal return:error];
+ }];
+ }]
+ deliverOn:RACScheduler.mainThreadScheduler]
+ publish];
+
+ _errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self];
+ [errorsConnection connect];
+
+ RACSignal *immediateExecuting = [RACObserve(self, activeExecutionSignals) map:^(NSArray *activeSignals) {
+ return @(activeSignals.count > 0);
+ }];
+
+ _executing = [[[[[immediateExecuting
+ deliverOn:RACScheduler.mainThreadScheduler]
+ // This is useful before the first value arrives on the main thread.
+ startWith:@NO]
+ distinctUntilChanged]
+ replayLast]
+ setNameWithFormat:@"%@ -executing", self];
+
+ RACSignal *moreExecutionsAllowed = [RACSignal
+ if:RACObserve(self, allowsConcurrentExecution)
+ then:[RACSignal return:@YES]
+ else:[immediateExecuting not]];
+
+ if (enabledSignal == nil) {
+ enabledSignal = [RACSignal return:@YES];
+ } else {
+ enabledSignal = [[[enabledSignal
+ startWith:@YES]
+ takeUntil:self.rac_willDeallocSignal]
+ replayLast];
+ }
+
+ _immediateEnabled = [[RACSignal
+ combineLatest:@[ enabledSignal, moreExecutionsAllowed ]]
+ and];
+
+ _enabled = [[[[[self.immediateEnabled
+ take:1]
+ concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]]
+ distinctUntilChanged]
+ replayLast]
+ setNameWithFormat:@"%@ -enabled", self];
+
+ return self;
+}
+
+#pragma mark Execution
+
+- (RACSignal *)execute:(id)input {
+ // `immediateEnabled` is guaranteed to send a value upon subscription, so
+ // -first is acceptable here.
+ BOOL enabled = [[self.immediateEnabled first] boolValue];
+ if (!enabled) {
+ NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil),
+ RACUnderlyingCommandErrorKey: self
+ }];
+
+ return [RACSignal error:error];
+ }
+
+ RACSignal *signal = self.signalBlock(input);
+ NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input);
+
+ // We subscribe to the signal on the main thread so that it occurs _after_
+ // -addActiveExecutionSignal: completes below.
+ //
+ // This means that `executing` and `enabled` will send updated values before
+ // the signal actually starts performing work.
+ RACMulticastConnection *connection = [[signal
+ subscribeOn:RACScheduler.mainThreadScheduler]
+ multicast:[RACReplaySubject subject]];
+
+ @weakify(self);
+
+ [self addActiveExecutionSignal:connection.signal];
+ [connection.signal subscribeError:^(NSError *error) {
+ @strongify(self);
+ [self removeActiveExecutionSignal:connection.signal];
+ } completed:^{
+ @strongify(self);
+ [self removeActiveExecutionSignal:connection.signal];
+ }];
+
+ [connection connect];
+ return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, [input rac_description]];
+}
+
+#pragma mark NSKeyValueObserving
+
++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
+ // Generate all KVO notifications manually to avoid the performance impact
+ // of unnecessary swizzling.
+ return NO;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.h b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.h
new file mode 100644
index 0000000..bb25f7d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.h
@@ -0,0 +1,48 @@
+//
+// RACCompoundDisposable.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDisposable.h"
+
+/// A disposable of disposables. When it is disposed, it disposes of all its
+/// contained disposables.
+///
+/// If -addDisposable: is called after the compound disposable has been disposed
+/// of, the given disposable is immediately disposed. This allows a compound
+/// disposable to act as a stand-in for a disposable that will be delivered
+/// asynchronously.
+@interface RACCompoundDisposable : RACDisposable
+
+/// Creates and returns a new compound disposable.
++ (instancetype)compoundDisposable;
+
+/// Creates and returns a new compound disposable containing the given
+/// disposables.
++ (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables;
+
+/// Adds the given disposable. If the receiving disposable has already been
+/// disposed of, the given disposable is disposed immediately.
+///
+/// This method is thread-safe.
+///
+/// disposable - The disposable to add. This may be nil, in which case nothing
+/// happens.
+- (void)addDisposable:(RACDisposable *)disposable;
+
+/// Removes the specified disposable from the compound disposable (regardless of
+/// its disposed status), or does nothing if it's not in the compound disposable.
+///
+/// This is mainly useful for limiting the memory usage of the compound
+/// disposable for long-running operations.
+///
+/// This method is thread-safe.
+///
+/// disposable - The disposable to remove. This argument may be nil (to make the
+/// use of weak references easier).
+- (void)removeDisposable:(RACDisposable *)disposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.m b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.m
new file mode 100644
index 0000000..3b0471c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.m
@@ -0,0 +1,239 @@
+//
+// RACCompoundDisposable.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACCompoundDisposable.h"
+#import "RACCompoundDisposableProvider.h"
+#import <libkern/OSAtomic.h>
+
+// The number of child disposables for which space will be reserved directly in
+// `RACCompoundDisposable`.
+//
+// This number has been empirically determined to provide a good tradeoff
+// between performance, memory usage, and `RACCompoundDisposable` instance size
+// in a moderately complex GUI application.
+//
+// Profile any change!
+#define RACCompoundDisposableInlineCount 2
+
+static CFMutableArrayRef RACCreateDisposablesArray(void) {
+ // Compare values using only pointer equality.
+ CFArrayCallBacks callbacks = kCFTypeArrayCallBacks;
+ callbacks.equal = NULL;
+
+ return CFArrayCreateMutable(NULL, 0, &callbacks);
+}
+
+@interface RACCompoundDisposable () {
+ // Used for synchronization.
+ OSSpinLock _spinLock;
+
+ #if RACCompoundDisposableInlineCount
+ // A fast array to the first N of the receiver's disposables.
+ //
+ // Once this is full, `_disposables` will be created and used for additional
+ // disposables.
+ //
+ // This array should only be manipulated while _spinLock is held.
+ RACDisposable *_inlineDisposables[RACCompoundDisposableInlineCount];
+ #endif
+
+ // Contains the receiver's disposables.
+ //
+ // This array should only be manipulated while _spinLock is held. If
+ // `_disposed` is YES, this may be NULL.
+ CFMutableArrayRef _disposables;
+
+ // Whether the receiver has already been disposed.
+ //
+ // This ivar should only be accessed while _spinLock is held.
+ BOOL _disposed;
+}
+
+@end
+
+@implementation RACCompoundDisposable
+
+#pragma mark Properties
+
+- (BOOL)isDisposed {
+ OSSpinLockLock(&_spinLock);
+ BOOL disposed = _disposed;
+ OSSpinLockUnlock(&_spinLock);
+
+ return disposed;
+}
+
+#pragma mark Lifecycle
+
++ (instancetype)compoundDisposable {
+ return [[self alloc] initWithDisposables:nil];
+}
+
++ (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables {
+ return [[self alloc] initWithDisposables:disposables];
+}
+
+- (id)initWithDisposables:(NSArray *)otherDisposables {
+ self = [self init];
+ if (self == nil) return nil;
+
+ #if RACCompoundDisposableInlineCount
+ [otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) {
+ _inlineDisposables[index] = disposable;
+
+ // Stop after this iteration if we've reached the end of the inlined
+ // array.
+ if (index == RACCompoundDisposableInlineCount - 1) *stop = YES;
+ }];
+ #endif
+
+ if (otherDisposables.count > RACCompoundDisposableInlineCount) {
+ _disposables = RACCreateDisposablesArray();
+
+ CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount);
+ CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range);
+ }
+
+ return self;
+}
+
+- (id)initWithBlock:(void (^)(void))block {
+ RACDisposable *disposable = [RACDisposable disposableWithBlock:block];
+ return [self initWithDisposables:@[ disposable ]];
+}
+
+- (void)dealloc {
+ #if RACCompoundDisposableInlineCount
+ for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
+ _inlineDisposables[i] = nil;
+ }
+ #endif
+
+ if (_disposables != NULL) {
+ CFRelease(_disposables);
+ _disposables = NULL;
+ }
+}
+
+#pragma mark Addition and Removal
+
+- (void)addDisposable:(RACDisposable *)disposable {
+ NSCParameterAssert(disposable != self);
+ if (disposable == nil) return;
+
+ BOOL shouldDispose = NO;
+
+ OSSpinLockLock(&_spinLock);
+ {
+ if (_disposed) {
+ shouldDispose = YES;
+ } else {
+ #if RACCompoundDisposableInlineCount
+ for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
+ if (_inlineDisposables[i] == nil) {
+ _inlineDisposables[i] = disposable;
+ goto foundSlot;
+ }
+ }
+ #endif
+
+ if (_disposables == NULL) _disposables = RACCreateDisposablesArray();
+ CFArrayAppendValue(_disposables, (__bridge void *)disposable);
+
+ if (RACCOMPOUNDDISPOSABLE_ADDED_ENABLED()) {
+ RACCOMPOUNDDISPOSABLE_ADDED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
+ }
+
+ #if RACCompoundDisposableInlineCount
+ foundSlot:;
+ #endif
+ }
+ }
+ OSSpinLockUnlock(&_spinLock);
+
+ // Performed outside of the lock in case the compound disposable is used
+ // recursively.
+ if (shouldDispose) [disposable dispose];
+}
+
+- (void)removeDisposable:(RACDisposable *)disposable {
+ if (disposable == nil) return;
+
+ OSSpinLockLock(&_spinLock);
+ {
+ if (!_disposed) {
+ #if RACCompoundDisposableInlineCount
+ for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
+ if (_inlineDisposables[i] == disposable) _inlineDisposables[i] = nil;
+ }
+ #endif
+
+ if (_disposables != NULL) {
+ CFIndex count = CFArrayGetCount(_disposables);
+ for (CFIndex i = count - 1; i >= 0; i--) {
+ const void *item = CFArrayGetValueAtIndex(_disposables, i);
+ if (item == (__bridge void *)disposable) {
+ CFArrayRemoveValueAtIndex(_disposables, i);
+ }
+ }
+
+ if (RACCOMPOUNDDISPOSABLE_REMOVED_ENABLED()) {
+ RACCOMPOUNDDISPOSABLE_REMOVED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
+ }
+ }
+ }
+ }
+ OSSpinLockUnlock(&_spinLock);
+}
+
+#pragma mark RACDisposable
+
+static void disposeEach(const void *value, void *context) {
+ RACDisposable *disposable = (__bridge id)value;
+ [disposable dispose];
+}
+
+- (void)dispose {
+ #if RACCompoundDisposableInlineCount
+ RACDisposable *inlineCopy[RACCompoundDisposableInlineCount];
+ #endif
+
+ CFArrayRef remainingDisposables = NULL;
+
+ OSSpinLockLock(&_spinLock);
+ {
+ _disposed = YES;
+
+ #if RACCompoundDisposableInlineCount
+ for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
+ inlineCopy[i] = _inlineDisposables[i];
+ _inlineDisposables[i] = nil;
+ }
+ #endif
+
+ remainingDisposables = _disposables;
+ _disposables = NULL;
+ }
+ OSSpinLockUnlock(&_spinLock);
+
+ #if RACCompoundDisposableInlineCount
+ // Dispose outside of the lock in case the compound disposable is used
+ // recursively.
+ for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
+ [inlineCopy[i] dispose];
+ }
+ #endif
+
+ if (remainingDisposables == NULL) return;
+
+ CFIndex count = CFArrayGetCount(remainingDisposables);
+ CFArrayApplyFunction(remainingDisposables, CFRangeMake(0, count), &disposeEach, NULL);
+ CFRelease(remainingDisposables);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposableProvider.d b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposableProvider.d
new file mode 100644
index 0000000..847db19
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposableProvider.d
@@ -0,0 +1,4 @@
+provider RACCompoundDisposable {
+ probe added(char *compoundDisposable, char *disposable, long newTotal);
+ probe removed(char *compoundDisposable, char *disposable, long newTotal);
+};
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.h b/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.h
new file mode 100644
index 0000000..9ec96bd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.h
@@ -0,0 +1,28 @@
+//
+// RACDelegateProxy.h
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/19/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACSignal;
+
+// A private delegate object suitable for using
+// -rac_signalForSelector:fromProtocol: upon.
+@interface RACDelegateProxy : NSObject
+
+// The delegate to which messages should be forwarded if not handled by
+// any -signalForSelector: applications.
+@property (nonatomic, unsafe_unretained) id rac_proxiedDelegate;
+
+// Creates a delegate proxy capable of responding to selectors from `protocol`.
+- (instancetype)initWithProtocol:(Protocol *)protocol;
+
+// Calls -rac_signalForSelector:fromProtocol: using the `protocol` specified
+// during initialization.
+- (RACSignal *)signalForSelector:(SEL)selector;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.m b/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.m
new file mode 100644
index 0000000..1d4fcbc
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.m
@@ -0,0 +1,78 @@
+//
+// RACDelegateProxy.m
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/19/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDelegateProxy.h"
+#import "RACSignal+Operations.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "NSObject+RACDeallocating.h"
+#import <objc/runtime.h>
+
+@interface RACDelegateProxy () {
+ // Declared as an ivar to avoid method naming conflicts.
+ Protocol *_protocol;
+}
+
+@end
+
+@implementation RACDelegateProxy
+
+#pragma mark Lifecycle
+
+- (instancetype)initWithProtocol:(Protocol *)protocol {
+ NSCParameterAssert(protocol != NULL);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ class_addProtocol(self.class, protocol);
+
+ _protocol = protocol;
+
+ return self;
+}
+
+#pragma mark API
+
+- (RACSignal *)signalForSelector:(SEL)selector {
+ return [self rac_signalForSelector:selector fromProtocol:_protocol];
+}
+
+#pragma mark NSObject
+
+- (BOOL)isProxy {
+ return YES;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+ [invocation invokeWithTarget:self.rac_proxiedDelegate];
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
+ // Look for the selector as an optional instance method.
+ struct objc_method_description methodDescription = protocol_getMethodDescription(_protocol, selector, NO, YES);
+
+ if (methodDescription.name == NULL) {
+ // Then fall back to looking for a required instance
+ // method.
+ methodDescription = protocol_getMethodDescription(_protocol, selector, YES, YES);
+ if (methodDescription.name == NULL) return [super methodSignatureForSelector:selector];
+ }
+
+ return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
+}
+
+- (BOOL)respondsToSelector:(SEL)selector {
+ // Add the delegate to the autorelease pool, so it doesn't get deallocated
+ // between this method call and -forwardInvocation:.
+ __autoreleasing id delegate = self.rac_proxiedDelegate;
+ if ([delegate respondsToSelector:selector]) return YES;
+
+ return [super respondsToSelector:selector];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h b/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h
new file mode 100644
index 0000000..1267d44
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h
@@ -0,0 +1,36 @@
+//
+// RACDisposable.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACScopedDisposable;
+
+
+/// A disposable encapsulates the work necessary to tear down and cleanup a
+/// subscription.
+@interface RACDisposable : NSObject
+
+/// Whether the receiver has been disposed.
+///
+/// Use of this property is discouraged, since it may be set to `YES`
+/// concurrently at any time.
+///
+/// This property is not KVO-compliant.
+@property (atomic, assign, getter = isDisposed, readonly) BOOL disposed;
+
++ (instancetype)disposableWithBlock:(void (^)(void))block;
+
+/// Performs the disposal work. Can be called multiple times, though subsequent
+/// calls won't do anything.
+- (void)dispose;
+
+/// Returns a new disposable which will dispose of this disposable when it gets
+/// dealloc'd.
+- (RACScopedDisposable *)asScopedDisposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.m b/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.m
new file mode 100644
index 0000000..08eceb2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.m
@@ -0,0 +1,92 @@
+//
+// RACDisposable.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDisposable.h"
+#import "RACScopedDisposable.h"
+#import <libkern/OSAtomic.h>
+
+@interface RACDisposable () {
+ // A copied block of type void (^)(void) containing the logic for disposal,
+ // a pointer to `self` if no logic should be performed upon disposal, or
+ // NULL if the receiver is already disposed.
+ //
+ // This should only be used atomically.
+ void * volatile _disposeBlock;
+}
+
+@end
+
+@implementation RACDisposable
+
+#pragma mark Properties
+
+- (BOOL)isDisposed {
+ return _disposeBlock == NULL;
+}
+
+#pragma mark Lifecycle
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _disposeBlock = (__bridge void *)self;
+ OSMemoryBarrier();
+
+ return self;
+}
+
+- (id)initWithBlock:(void (^)(void))block {
+ NSCParameterAssert(block != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _disposeBlock = (void *)CFBridgingRetain([block copy]);
+ OSMemoryBarrier();
+
+ return self;
+}
+
++ (instancetype)disposableWithBlock:(void (^)(void))block {
+ return [[self alloc] initWithBlock:block];
+}
+
+- (void)dealloc {
+ if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return;
+
+ CFRelease(_disposeBlock);
+ _disposeBlock = NULL;
+}
+
+#pragma mark Disposal
+
+- (void)dispose {
+ void (^disposeBlock)(void) = NULL;
+
+ while (YES) {
+ void *blockPtr = _disposeBlock;
+ if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) {
+ if (blockPtr != (__bridge void *)self) {
+ disposeBlock = CFBridgingRelease(blockPtr);
+ }
+
+ break;
+ }
+ }
+
+ if (disposeBlock != nil) disposeBlock();
+}
+
+#pragma mark Scoped Disposables
+
+- (RACScopedDisposable *)asScopedDisposable {
+ return [RACScopedDisposable scopedDisposableWithDisposable:self];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.h
new file mode 100644
index 0000000..1e103e3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.h
@@ -0,0 +1,20 @@
+//
+// RACDynamicSequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class that implements a sequence dynamically using blocks.
+@interface RACDynamicSequence : RACSequence
+
+// Returns a sequence which evaluates `dependencyBlock` only once, the first
+// time either `headBlock` or `tailBlock` is evaluated. The result of
+// `dependencyBlock` will be passed into `headBlock` and `tailBlock` when
+// invoked.
++ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.m
new file mode 100644
index 0000000..48a977b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.m
@@ -0,0 +1,197 @@
+//
+// RACDynamicSequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACDynamicSequence.h"
+#import <libkern/OSAtomic.h>
+
+// Determines how RACDynamicSequences will be deallocated before the next one is
+// shifted onto the autorelease pool.
+//
+// This avoids stack overflows when deallocating long chains of dynamic
+// sequences.
+#define DEALLOC_OVERFLOW_GUARD 100
+
+@interface RACDynamicSequence () {
+ // The value for the "head" property, if it's been evaluated already.
+ //
+ // Because it's legal for head to be nil, this ivar is valid any time
+ // headBlock is nil.
+ //
+ // This ivar should only be accessed while synchronized on self.
+ id _head;
+
+ // The value for the "tail" property, if it's been evaluated already.
+ //
+ // Because it's legal for tail to be nil, this ivar is valid any time
+ // tailBlock is nil.
+ //
+ // This ivar should only be accessed while synchronized on self.
+ RACSequence *_tail;
+
+ // The result of an evaluated `dependencyBlock`.
+ //
+ // This ivar is valid any time `hasDependency` is YES and `dependencyBlock`
+ // is nil.
+ //
+ // This ivar should only be accessed while synchronized on self.
+ id _dependency;
+}
+
+// A block used to evaluate head. This should be set to nil after `_head` has been
+// initialized.
+//
+// This is marked `strong` instead of `copy` because of some bizarre block
+// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
+//
+// The signature of this block varies based on the value of `hasDependency`:
+//
+// - If YES, this block is of type `id (^)(id)`.
+// - If NO, this block is of type `id (^)(void)`.
+//
+// This property should only be accessed while synchronized on self.
+@property (nonatomic, strong) id headBlock;
+
+// A block used to evaluate tail. This should be set to nil after `_tail` has been
+// initialized.
+//
+// This is marked `strong` instead of `copy` because of some bizarre block
+// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
+//
+// The signature of this block varies based on the value of `hasDependency`:
+//
+// - If YES, this block is of type `RACSequence * (^)(id)`.
+// - If NO, this block is of type `RACSequence * (^)(void)`.
+//
+// This property should only be accessed while synchronized on self.
+@property (nonatomic, strong) id tailBlock;
+
+// Whether the receiver was initialized with a `dependencyBlock`.
+//
+// This property should only be accessed while synchronized on self.
+@property (nonatomic, assign) BOOL hasDependency;
+
+// A dependency which must be evaluated before `headBlock` and `tailBlock`. This
+// should be set to nil after `_dependency` and `dependencyBlockExecuted` have
+// been set.
+//
+// This is marked `strong` instead of `copy` because of some bizarre block
+// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506.
+//
+// This property should only be accessed while synchronized on self.
+@property (nonatomic, strong) id (^dependencyBlock)(void);
+
+@end
+
+@implementation RACDynamicSequence
+
+#pragma mark Lifecycle
+
++ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock {
+ NSCParameterAssert(headBlock != nil);
+
+ RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
+ seq.headBlock = [headBlock copy];
+ seq.tailBlock = [tailBlock copy];
+ seq.hasDependency = NO;
+ return seq;
+}
+
++ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock {
+ NSCParameterAssert(dependencyBlock != nil);
+ NSCParameterAssert(headBlock != nil);
+
+ RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
+ seq.headBlock = [headBlock copy];
+ seq.tailBlock = [tailBlock copy];
+ seq.dependencyBlock = [dependencyBlock copy];
+ seq.hasDependency = YES;
+ return seq;
+}
+
+- (void)dealloc {
+ static volatile int32_t directDeallocCount = 0;
+
+ if (OSAtomicIncrement32(&directDeallocCount) >= DEALLOC_OVERFLOW_GUARD) {
+ OSAtomicAdd32(-DEALLOC_OVERFLOW_GUARD, &directDeallocCount);
+
+ // Put this sequence's tail onto the autorelease pool so we stop
+ // recursing.
+ __autoreleasing RACSequence *tail __attribute__((unused)) = _tail;
+ }
+
+ _tail = nil;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ @synchronized (self) {
+ id untypedHeadBlock = self.headBlock;
+ if (untypedHeadBlock == nil) return _head;
+
+ if (self.hasDependency) {
+ if (self.dependencyBlock != nil) {
+ _dependency = self.dependencyBlock();
+ self.dependencyBlock = nil;
+ }
+
+ id (^headBlock)(id) = untypedHeadBlock;
+ _head = headBlock(_dependency);
+ } else {
+ id (^headBlock)(void) = untypedHeadBlock;
+ _head = headBlock();
+ }
+
+ self.headBlock = nil;
+ return _head;
+ }
+}
+
+- (RACSequence *)tail {
+ @synchronized (self) {
+ id untypedTailBlock = self.tailBlock;
+ if (untypedTailBlock == nil) return _tail;
+
+ if (self.hasDependency) {
+ if (self.dependencyBlock != nil) {
+ _dependency = self.dependencyBlock();
+ self.dependencyBlock = nil;
+ }
+
+ RACSequence * (^tailBlock)(id) = untypedTailBlock;
+ _tail = tailBlock(_dependency);
+ } else {
+ RACSequence * (^tailBlock)(void) = untypedTailBlock;
+ _tail = tailBlock();
+ }
+
+ if (_tail.name == nil) _tail.name = self.name;
+
+ self.tailBlock = nil;
+ return _tail;
+ }
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ id head = @"(unresolved)";
+ id tail = @"(unresolved)";
+
+ @synchronized (self) {
+ if (self.headBlock == nil) head = _head;
+ if (self.tailBlock == nil) {
+ tail = _tail;
+ if (tail == self) tail = @"(self)";
+ }
+ }
+
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@, tail = %@ }", self.class, self, self.name, head, tail];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.h
new file mode 100644
index 0000000..81ac6db
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.h
@@ -0,0 +1,17 @@
+//
+// RACDynamicSignal.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+
+// A private `RACSignal` subclasses that implements its subscription behavior
+// using a block.
+@interface RACDynamicSignal : RACSignal
+
++ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.m
new file mode 100644
index 0000000..7a8ec32
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSignal.m
@@ -0,0 +1,189 @@
+//
+// RACDynamicSignal.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDynamicSignal.h"
+#import "EXTScope.h"
+#import "RACPassthroughSubscriber.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriber.h"
+#import "RACCompoundDisposable.h"
+#import <libkern/OSAtomic.h>
+
+// Retains dynamic signals while they wait for subscriptions.
+//
+// This set must only be used on the main thread.
+static CFMutableSetRef RACActiveSignals = nil;
+
+// A linked list of RACDynamicSignals, used in RACActiveSignalsToCheck.
+typedef struct RACSignalList {
+ CFTypeRef retainedSignal;
+ struct RACSignalList * restrict next;
+} RACSignalList;
+
+// An atomic queue of signals to check for subscribers. If any signals with zero
+// subscribers are found in this queue, they are removed from RACActiveSignals.
+static OSQueueHead RACActiveSignalsToCheck = OS_ATOMIC_QUEUE_INIT;
+
+// Whether RACActiveSignalsToCheck will be enumerated on the next iteration on
+// the main run loop.
+static volatile uint32_t RACWillCheckActiveSignals = 0;
+
+@interface RACDynamicSignal () {
+ // Contains all subscribers to the receiver.
+ //
+ // All access to this array must be synchronized using `_subscribersLock`.
+ NSMutableArray *_subscribers;
+
+ // Synchronizes access to `_subscribers`.
+ OSSpinLock _subscribersLock;
+}
+
+// The block to invoke for each subscriber.
+@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
+
+@end
+
+@implementation RACDynamicSignal
+
+#pragma mark Lifecycle
+
++ (void)initialize {
+ if (self != RACDynamicSignal.class) return;
+
+ CFSetCallBacks callbacks = kCFTypeSetCallBacks;
+
+ // Use pointer equality and hashes for membership testing.
+ callbacks.equal = NULL;
+ callbacks.hash = NULL;
+
+ RACActiveSignals = CFSetCreateMutable(NULL, 0, &callbacks);
+}
+
++ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
+ RACDynamicSignal *signal = [[self alloc] init];
+ signal->_didSubscribe = [didSubscribe copy];
+ return [signal setNameWithFormat:@"+createSignal:"];
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ // As soon as we're created we're already trying to be released. Such is life.
+ [self invalidateGlobalRefIfNoNewSubscribersShowUp];
+
+ return self;
+}
+
+static void RACCheckActiveSignals(void) {
+ // Clear this flag now, so another thread can re-dispatch to the main queue
+ // as needed.
+ OSAtomicAnd32Barrier(0, &RACWillCheckActiveSignals);
+
+ RACSignalList * restrict elem;
+
+ while ((elem = OSAtomicDequeue(&RACActiveSignalsToCheck, offsetof(RACSignalList, next))) != NULL) {
+ RACDynamicSignal *signal = CFBridgingRelease(elem->retainedSignal);
+ free(elem);
+
+ if (signal.hasSubscribers) {
+ // We want to keep the signal around until all its subscribers are done
+ CFSetAddValue(RACActiveSignals, (__bridge void *)signal);
+ } else {
+ CFSetRemoveValue(RACActiveSignals, (__bridge void *)signal);
+ }
+ }
+}
+
+- (void)invalidateGlobalRefIfNoNewSubscribersShowUp {
+ // If no one subscribes in one pass of the main run loop, then we're free to
+ // go. It's up to the caller to keep us alive if they still want us.
+ RACSignalList *elem = malloc(sizeof(*elem));
+
+ // This also serves to retain the signal until the next pass.
+ elem->retainedSignal = CFBridgingRetain(self);
+ OSAtomicEnqueue(&RACActiveSignalsToCheck, elem, offsetof(RACSignalList, next));
+
+ // Not using a barrier because duplicate scheduling isn't erroneous, just
+ // less optimized.
+ int32_t willCheck = OSAtomicOr32Orig(1, &RACWillCheckActiveSignals);
+
+ // Only schedule a check if RACWillCheckActiveSignals was 0 before.
+ if (willCheck == 0) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ RACCheckActiveSignals();
+ });
+ }
+}
+
+#pragma mark Managing Subscribers
+
+- (BOOL)hasSubscribers {
+ OSSpinLockLock(&_subscribersLock);
+ BOOL hasSubscribers = _subscribers.count > 0;
+ OSSpinLockUnlock(&_subscribersLock);
+
+ return hasSubscribers;
+}
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCParameterAssert(subscriber != nil);
+
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+ subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
+
+ OSSpinLockLock(&_subscribersLock);
+ if (_subscribers == nil) {
+ _subscribers = [NSMutableArray arrayWithObject:subscriber];
+ } else {
+ [_subscribers addObject:subscriber];
+ }
+ OSSpinLockUnlock(&_subscribersLock);
+
+ @weakify(self);
+ RACDisposable *defaultDisposable = [RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ if (self == nil) return;
+
+ BOOL stillHasSubscribers = YES;
+
+ OSSpinLockLock(&_subscribersLock);
+ {
+ // Since newer subscribers are generally shorter-lived, search
+ // starting from the end of the list.
+ NSUInteger index = [_subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
+ return obj == subscriber;
+ }];
+
+ if (index != NSNotFound) {
+ [_subscribers removeObjectAtIndex:index];
+ stillHasSubscribers = _subscribers.count > 0;
+ }
+ }
+ OSSpinLockUnlock(&_subscribersLock);
+
+ if (!stillHasSubscribers) {
+ [self invalidateGlobalRefIfNoNewSubscribersShowUp];
+ }
+ }];
+
+ [disposable addDisposable:defaultDisposable];
+
+ if (self.didSubscribe != NULL) {
+ RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
+ RACDisposable *innerDisposable = self.didSubscribe(subscriber);
+ [disposable addDisposable:innerDisposable];
+ }];
+
+ [disposable addDisposable:schedulingDisposable];
+ }
+
+ return disposable;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.h
new file mode 100644
index 0000000..ab5aa9f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.h
@@ -0,0 +1,14 @@
+//
+// RACEagerSequence.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 02/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACArraySequence.h"
+
+// Private class that implements an eager sequence.
+@interface RACEagerSequence : RACArraySequence
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.m
new file mode 100644
index 0000000..f12fbe9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.m
@@ -0,0 +1,66 @@
+//
+// RACEagerSequence.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 02/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACEagerSequence.h"
+#import "NSObject+RACDescription.h"
+#import "RACArraySequence.h"
+
+@implementation RACEagerSequence
+
+#pragma mark RACStream
+
++ (instancetype)return:(id)value {
+ return [[self sequenceWithArray:@[ value ] offset:0] setNameWithFormat:@"+return: %@", [value rac_description]];
+}
+
+- (instancetype)bind:(RACStreamBindBlock (^)(void))block {
+ NSCParameterAssert(block != nil);
+ RACStreamBindBlock bindBlock = block();
+ NSArray *currentArray = self.array;
+ NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:currentArray.count];
+
+ for (id value in currentArray) {
+ BOOL stop = NO;
+ RACSequence *boundValue = (id)bindBlock(value, &stop);
+ if (boundValue == nil) break;
+
+ for (id x in boundValue) {
+ [resultArray addObject:x];
+ }
+
+ if (stop) break;
+ }
+
+ return [[self.class sequenceWithArray:resultArray offset:0] setNameWithFormat:@"[%@] -bind:", self.name];
+}
+
+- (instancetype)concat:(RACSequence *)sequence {
+ NSCParameterAssert(sequence != nil);
+ NSCParameterAssert([sequence isKindOfClass:RACSequence.class]);
+
+ NSArray *array = [self.array arrayByAddingObjectsFromArray:sequence.array];
+ return [[self.class sequenceWithArray:array offset:0] setNameWithFormat:@"[%@] -concat: %@", self.name, sequence];
+}
+
+#pragma mark Extended methods
+
+- (RACSequence *)eagerSequence {
+ return self;
+}
+
+- (RACSequence *)lazySequence {
+ return [RACArraySequence sequenceWithArray:self.array offset:0];
+}
+
+- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *rest))reduce {
+ return [super foldRightWithStart:start reduce:^(id first, RACSequence *rest) {
+ return reduce(first, rest.eagerSequence);
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.h
new file mode 100644
index 0000000..140c78b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.h
@@ -0,0 +1,14 @@
+//
+// RACEmptySequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class representing an empty sequence.
+@interface RACEmptySequence : RACSequence
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.m
new file mode 100644
index 0000000..e4df150
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.m
@@ -0,0 +1,71 @@
+//
+// RACEmptySequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACEmptySequence.h"
+
+@implementation RACEmptySequence
+
+#pragma mark Lifecycle
+
++ (instancetype)empty {
+ static id singleton;
+ static dispatch_once_t pred;
+
+ dispatch_once(&pred, ^{
+ singleton = [[self alloc] init];
+ });
+
+ return singleton;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ return nil;
+}
+
+- (RACSequence *)tail {
+ return nil;
+}
+
+- (RACSequence *)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence {
+ return passthroughSequence ?: self;
+}
+
+#pragma mark NSCoding
+
+- (Class)classForCoder {
+ // Empty sequences should be encoded as themselves, not array sequences.
+ return self.class;
+}
+
+- (id)initWithCoder:(NSCoder *)coder {
+ // Return the singleton.
+ return self.class.empty;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@ }", self.class, self, self.name];
+}
+
+- (NSUInteger)hash {
+ // This hash isn't ideal, but it's better than -[RACSequence hash], which
+ // would just be zero because we have no head.
+ return (NSUInteger)(__bridge void *)self;
+}
+
+- (BOOL)isEqual:(RACSequence *)seq {
+ return (self == seq);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.h
new file mode 100644
index 0000000..90b4f00
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.h
@@ -0,0 +1,17 @@
+//
+// RACEmptySignal.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+
+// A private `RACSignal` subclasses that synchronously sends completed to any
+// subscribers.
+@interface RACEmptySignal : RACSignal
+
++ (RACSignal *)empty;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.m
new file mode 100644
index 0000000..8bc8f41
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEmptySignal.m
@@ -0,0 +1,62 @@
+//
+// RACEmptySignal.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACEmptySignal.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriber.h"
+
+@implementation RACEmptySignal
+
+#pragma mark Properties
+
+// Only allow this signal's name to be customized in DEBUG, since it's
+// a singleton in release builds (see +empty).
+- (void)setName:(NSString *)name {
+#ifdef DEBUG
+ [super setName:name];
+#endif
+}
+
+- (NSString *)name {
+#ifdef DEBUG
+ return super.name;
+#else
+ return @"+empty";
+#endif
+}
+
+#pragma mark Lifecycle
+
++ (RACSignal *)empty {
+#ifdef DEBUG
+ // Create multiple instances of this class in DEBUG so users can set custom
+ // names on each.
+ return [[[self alloc] init] setNameWithFormat:@"+empty"];
+#else
+ static id singleton;
+ static dispatch_once_t pred;
+
+ dispatch_once(&pred, ^{
+ singleton = [[self alloc] init];
+ });
+
+ return singleton;
+#endif
+}
+
+#pragma mark Subscription
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCParameterAssert(subscriber != nil);
+
+ return [RACScheduler.subscriptionScheduler schedule:^{
+ [subscriber sendCompleted];
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.h
new file mode 100644
index 0000000..c942f9e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.h
@@ -0,0 +1,17 @@
+//
+// RACErrorSignal.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+
+// A private `RACSignal` subclasses that synchronously sends an error to any
+// subscribers.
+@interface RACErrorSignal : RACSignal
+
++ (RACSignal *)error:(NSError *)error;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.m
new file mode 100644
index 0000000..fd6778c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACErrorSignal.m
@@ -0,0 +1,47 @@
+//
+// RACErrorSignal.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACErrorSignal.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriber.h"
+
+@interface RACErrorSignal ()
+
+// The error to send upon subscription.
+@property (nonatomic, strong, readonly) NSError *error;
+
+@end
+
+@implementation RACErrorSignal
+
+#pragma mark Lifecycle
+
++ (RACSignal *)error:(NSError *)error {
+ RACErrorSignal *signal = [[self alloc] init];
+ signal->_error = error;
+
+#ifdef DEBUG
+ [signal setNameWithFormat:@"+error: %@", error];
+#else
+ signal.name = @"+error:";
+#endif
+
+ return signal;
+}
+
+#pragma mark Subscription
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCParameterAssert(subscriber != nil);
+
+ return [RACScheduler.subscriptionScheduler schedule:^{
+ [subscriber sendError:self.error];
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h b/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h
new file mode 100644
index 0000000..9e64e5a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h
@@ -0,0 +1,51 @@
+//
+// RACEvent.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-01-07.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/// Describes the type of a RACEvent.
+///
+/// RACEventTypeCompleted - A `completed` event.
+/// RACEventTypeError - An `error` event.
+/// RACEventTypeNext - A `next` event.
+typedef enum : NSUInteger {
+ RACEventTypeCompleted,
+ RACEventTypeError,
+ RACEventTypeNext
+} RACEventType;
+
+/// Represents an event sent by a RACSignal.
+///
+/// This corresponds to the `Notification` class in Rx.
+@interface RACEvent : NSObject <NSCopying>
+
+/// Returns a singleton RACEvent representing the `completed` event.
++ (instancetype)completedEvent;
+
+/// Returns a new event of type RACEventTypeError, containing the given error.
++ (instancetype)eventWithError:(NSError *)error;
+
+/// Returns a new event of type RACEventTypeNext, containing the given value.
++ (instancetype)eventWithValue:(id)value;
+
+/// The type of event represented by the receiver.
+@property (nonatomic, assign, readonly) RACEventType eventType;
+
+/// Returns whether the receiver is of type RACEventTypeCompleted or
+/// RACEventTypeError.
+@property (nonatomic, getter = isFinished, assign, readonly) BOOL finished;
+
+/// The error associated with an event of type RACEventTypeError. This will be
+/// nil for all other event types.
+@property (nonatomic, strong, readonly) NSError *error;
+
+/// The value associated with an event of type RACEventTypeNext. This will be
+/// nil for all other event types.
+@property (nonatomic, strong, readonly) id value;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.m b/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.m
new file mode 100644
index 0000000..270a95d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACEvent.m
@@ -0,0 +1,113 @@
+//
+// RACEvent.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-01-07.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACEvent.h"
+
+@interface RACEvent ()
+
+// An object associated with this event. This will be used for the error and
+// value properties.
+@property (nonatomic, strong, readonly) id object;
+
+// Initializes the receiver with the given type and object.
+- (id)initWithEventType:(RACEventType)type object:(id)object;
+
+@end
+
+@implementation RACEvent
+
+#pragma mark Properties
+
+- (BOOL)isFinished {
+ return self.eventType == RACEventTypeCompleted || self.eventType == RACEventTypeError;
+}
+
+- (NSError *)error {
+ return (self.eventType == RACEventTypeError ? self.object : nil);
+}
+
+- (id)value {
+ return (self.eventType == RACEventTypeNext ? self.object : nil);
+}
+
+#pragma mark Lifecycle
+
++ (instancetype)completedEvent {
+ static dispatch_once_t pred;
+ static id singleton;
+
+ dispatch_once(&pred, ^{
+ singleton = [[self alloc] initWithEventType:RACEventTypeCompleted object:nil];
+ });
+
+ return singleton;
+}
+
++ (instancetype)eventWithError:(NSError *)error {
+ return [[self alloc] initWithEventType:RACEventTypeError object:error];
+}
+
++ (instancetype)eventWithValue:(id)value {
+ return [[self alloc] initWithEventType:RACEventTypeNext object:value];
+}
+
+- (id)initWithEventType:(RACEventType)type object:(id)object {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _eventType = type;
+ _object = object;
+
+ return self;
+}
+
+#pragma mark NSCopying
+
+- (id)copyWithZone:(NSZone *)zone {
+ return self;
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ NSString *eventDescription = nil;
+
+ switch (self.eventType) {
+ case RACEventTypeCompleted:
+ eventDescription = @"completed";
+ break;
+
+ case RACEventTypeError:
+ eventDescription = [NSString stringWithFormat:@"error = %@", self.object];
+ break;
+
+ case RACEventTypeNext:
+ eventDescription = [NSString stringWithFormat:@"next = %@", self.object];
+ break;
+
+ default:
+ NSCAssert(NO, @"Unrecognized event type: %i", (int)self.eventType);
+ }
+
+ return [NSString stringWithFormat:@"<%@: %p>{ %@ }", self.class, self, eventDescription];
+}
+
+- (NSUInteger)hash {
+ return self.eventType ^ [self.object hash];
+}
+
+- (BOOL)isEqual:(id)event {
+ if (event == self) return YES;
+ if (![event isKindOfClass:RACEvent.class]) return NO;
+ if (self.eventType != [event eventType]) return NO;
+
+ // Catches the nil case too.
+ return self.object == [event object] || [self.object isEqual:[event object]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.h
new file mode 100644
index 0000000..697d922
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.h
@@ -0,0 +1,20 @@
+//
+// RACGroupedSignal.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubject.h"
+
+
+/// A grouped signal is used by -[RACSignal groupBy:transform:].
+@interface RACGroupedSignal : RACSubject
+
+/// The key shared by the group.
+@property (nonatomic, readonly, copy) id<NSCopying> key;
+
++ (instancetype)signalWithKey:(id<NSCopying>)key;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.m
new file mode 100644
index 0000000..00e0378
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.m
@@ -0,0 +1,25 @@
+//
+// RACGroupedSignal.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACGroupedSignal.h"
+
+@interface RACGroupedSignal ()
+@property (nonatomic, copy) id<NSCopying> key;
+@end
+
+@implementation RACGroupedSignal
+
+#pragma mark API
+
++ (instancetype)signalWithKey:(id<NSCopying>)key {
+ RACGroupedSignal *subject = [self subject];
+ subject.key = key;
+ return subject;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.h
new file mode 100644
index 0000000..76b5b50
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.h
@@ -0,0 +1,14 @@
+//
+// RACImmediateScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+
+// A private scheduler which immediately executes its scheduled blocks.
+@interface RACImmediateScheduler : RACScheduler
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.m
new file mode 100644
index 0000000..f32eda3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.m
@@ -0,0 +1,44 @@
+//
+// RACImmediateScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACImmediateScheduler.h"
+#import "RACScheduler+Private.h"
+
+@implementation RACImmediateScheduler
+
+#pragma mark Lifecycle
+
+- (id)init {
+ return [super initWithName:@"com.ReactiveCocoa.RACScheduler.immediateScheduler"];
+}
+
+#pragma mark RACScheduler
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ block();
+ return nil;
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(block != NULL);
+
+ [NSThread sleepUntilDate:date];
+ block();
+
+ return nil;
+}
+
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
+ NSCAssert(NO, @"+[RACScheduler immediateScheduler] does not support %@.", NSStringFromSelector(_cmd));
+ return nil;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.h
new file mode 100644
index 0000000..eab84a3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.h
@@ -0,0 +1,16 @@
+//
+// RACIndexSetSequence.h
+// ReactiveCocoa
+//
+// Created by Sergey Gavrilyuk on 12/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class that adapts an array to the RACSequence interface.
+@interface RACIndexSetSequence : RACSequence
+
++ (instancetype)sequenceWithIndexSet:(NSIndexSet *)indexSet;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.m
new file mode 100644
index 0000000..7ac8f53
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACIndexSetSequence.m
@@ -0,0 +1,108 @@
+//
+// RACIndexSetSequence.m
+// ReactiveCocoa
+//
+// Created by Sergey Gavrilyuk on 12/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACIndexSetSequence.h"
+
+@interface RACIndexSetSequence ()
+
+// A buffer holding the `NSUInteger` values to enumerate over.
+//
+// This is mostly used for memory management. Most access should go through
+// `indexes` instead.
+@property (nonatomic, strong, readonly) NSData *data;
+
+// The indexes that this sequence should enumerate.
+@property (nonatomic, readonly) const NSUInteger *indexes;
+
+// The number of indexes to enumerate.
+@property (nonatomic, readonly) NSUInteger count;
+
+@end
+
+@implementation RACIndexSetSequence
+
+#pragma mark Lifecycle
+
++ (instancetype)sequenceWithIndexSet:(NSIndexSet *)indexSet {
+ NSUInteger count = indexSet.count;
+ NSUInteger sizeInBytes = sizeof(NSUInteger) * count;
+
+ NSMutableData *data = [[NSMutableData alloc] initWithCapacity:sizeInBytes];
+ [indexSet getIndexes:data.mutableBytes maxCount:count inIndexRange:NULL];
+
+ RACIndexSetSequence *seq = [[self alloc] init];
+ seq->_data = data;
+ seq->_indexes = data.bytes;
+ seq->_count = count;
+ return seq;
+}
+
++ (instancetype)sequenceWithIndexSetSequence:(RACIndexSetSequence *)indexSetSequence offset:(NSUInteger)offset {
+ NSCParameterAssert(offset < indexSetSequence.count);
+
+ RACIndexSetSequence *seq = [[self alloc] init];
+ seq->_data = indexSetSequence.data;
+ seq->_indexes = indexSetSequence.indexes + offset;
+ seq->_count = indexSetSequence.count - offset;
+ return seq;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ return @(self.indexes[0]);
+}
+
+- (RACSequence *)tail {
+ if (self.count <= 1) return [RACSequence empty];
+
+ return [self.class sequenceWithIndexSetSequence:self offset:1];
+}
+
+#pragma mark NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id[])stackbuf count:(NSUInteger)len {
+ NSCParameterAssert(len > 0);
+
+ if (state->state >= self.count) {
+ // Enumeration has completed.
+ return 0;
+ }
+
+ if (state->state == 0) {
+ // Enumeration begun, mark the mutation flag.
+ state->mutationsPtr = state->extra;
+ }
+
+ state->itemsPtr = stackbuf;
+
+ unsigned long index = 0;
+ while (index < MIN(self.count - state->state, len)) {
+ stackbuf[index] = @(self.indexes[index + state->state]);
+ ++index;
+ }
+
+ state->state += index;
+ return index;
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ NSMutableString *indexesStr = [NSMutableString string];
+
+ for (unsigned int i = 0; i < self.count; ++i) {
+ if (i > 0) [indexesStr appendString:@", "];
+
+ [indexesStr appendFormat:@"%lu", (unsigned long)self.indexes[i]];
+ }
+
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, indexes = %@ }", self.class, self, self.name, indexesStr];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.h b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.h
new file mode 100644
index 0000000..0f72995
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.h
@@ -0,0 +1,97 @@
+//
+// RACKVOChannel.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 27/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACChannel.h"
+#import "EXTKeyPathCoding.h"
+#import "metamacros.h"
+
+/// Creates a RACKVOChannel to the given key path. When the targeted object
+/// deallocates, the channel will complete.
+///
+/// If RACChannelTo() is used as an expression, it returns a RACChannelTerminal that
+/// can be used to watch the specified property for changes, and set new values
+/// for it. The terminal will start with the property's current value upon
+/// subscription.
+///
+/// If RACChannelTo() is used on the left-hand side of an assignment, there must a
+/// RACChannelTerminal on the right-hand side of the assignment. The two will be
+/// subscribed to one another: the property's value is immediately set to the
+/// value of the channel terminal on the right-hand side, and subsequent changes
+/// to either terminal will be reflected on the other.
+///
+/// There are two different versions of this macro:
+///
+/// - RACChannelTo(TARGET, KEYPATH, NILVALUE) will create a channel to the `KEYPATH`
+/// of `TARGET`. If the terminal is ever sent a `nil` value, the property will
+/// be set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object
+/// properties, but an NSValue should be used for primitive properties, to
+/// avoid an exception if `nil` is sent (which might occur if an intermediate
+/// object is set to `nil`).
+/// - RACChannelTo(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to
+/// `nil`.
+///
+/// Examples
+///
+/// RACChannelTerminal *integerChannel = RACChannelTo(self, integerProperty, @42);
+///
+/// // Sets self.integerProperty to 5.
+/// [integerChannel sendNext:@5];
+///
+/// // Logs the current value of self.integerProperty, and all future changes.
+/// [integerChannel subscribeNext:^(id value) {
+/// NSLog(@"value: %@", value);
+/// }];
+///
+/// // Binds properties to each other, taking the initial value from the right
+/// side.
+/// RACChannelTo(view, objectProperty) = RACChannelTo(model, objectProperty);
+/// RACChannelTo(view, integerProperty, @2) = RACChannelTo(model, integerProperty, @10);
+#define RACChannelTo(TARGET, ...) \
+ metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
+ (RACChannelTo_(TARGET, __VA_ARGS__, nil)) \
+ (RACChannelTo_(TARGET, __VA_ARGS__))
+
+/// Do not use this directly. Use the RACChannelTo macro above.
+#define RACChannelTo_(TARGET, KEYPATH, NILVALUE) \
+ [[RACKVOChannel alloc] initWithTarget:(TARGET) keyPath:@keypath(TARGET, KEYPATH) nilValue:(NILVALUE)][@keypath(RACKVOChannel.new, followingTerminal)]
+
+/// A RACChannel that observes a KVO-compliant key path for changes.
+@interface RACKVOChannel : RACChannel
+
+/// Initializes a channel that will observe the given object and key path.
+///
+/// The current value of the key path, and future KVO notifications for the given
+/// key path, will be sent to subscribers of the channel's `followingTerminal`.
+/// Values sent to the `followingTerminal` will be set at the given key path using
+/// key-value coding.
+///
+/// When the target object deallocates, the channel will complete. Signal errors
+/// are considered undefined behavior.
+///
+/// This is the designated initializer for this class.
+///
+/// target - The object to bind to.
+/// keyPath - The key path to observe and set the value of.
+/// nilValue - The value to set at the key path whenever a `nil` value is
+/// received. This may be nil when connecting to object properties, but
+/// an NSValue should be used for primitive properties, to avoid an
+/// exception if `nil` is received (which might occur if an intermediate
+/// object is set to `nil`).
+- (id)initWithTarget:(NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue;
+
+- (id)init __attribute__((unavailable("Use -initWithTarget:keyPath:nilValue: instead")));
+
+@end
+
+/// Methods needed for the convenience macro. Do not call explicitly.
+@interface RACKVOChannel (RACChannelTo)
+
+- (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.m b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.m
new file mode 100644
index 0000000..7292a74
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOChannel.m
@@ -0,0 +1,206 @@
+//
+// RACKVOChannel.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 27/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACKVOChannel.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACKVOWrapper.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "NSString+RACKeyPathUtilities.h"
+#import "RACChannel.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACReplaySubject.h"
+#import "RACSignal+Operations.h"
+#import "RACSubscriber+Private.h"
+#import "RACSubject.h"
+
+// Key for the array of RACKVOChannel's additional thread local
+// data in the thread dictionary.
+static NSString * const RACKVOChannelDataDictionaryKey = @"RACKVOChannelKey";
+
+// Wrapper class for additional thread local data.
+@interface RACKVOChannelData : NSObject
+
+// The flag used to ignore updates the channel itself has triggered.
+@property (nonatomic, assign) BOOL ignoreNextUpdate;
+
+// A pointer to the owner of the data. Only use this for pointer comparison,
+// never as an object reference.
+@property (nonatomic, assign) void *owner;
+
++ (instancetype)dataForChannel:(RACKVOChannel *)channel;
+
+@end
+
+@interface RACKVOChannel ()
+
+// The object whose key path the channel is wrapping.
+@property (atomic, unsafe_unretained) NSObject *target;
+
+// The key path the channel is wrapping.
+@property (nonatomic, copy, readonly) NSString *keyPath;
+
+// Returns the existing thread local data container or nil if none exists.
+@property (nonatomic, strong, readonly) RACKVOChannelData *currentThreadData;
+
+// Creates the thread local data container for the channel.
+- (void)createCurrentThreadData;
+
+// Destroy the thread local data container for the channel.
+- (void)destroyCurrentThreadData;
+
+@end
+
+@implementation RACKVOChannel
+
+#pragma mark Properties
+
+- (RACKVOChannelData *)currentThreadData {
+ NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey];
+
+ for (RACKVOChannelData *data in dataArray) {
+ if (data.owner == (__bridge void *)self) return data;
+ }
+
+ return nil;
+}
+
+#pragma mark Lifecycle
+
+- (id)initWithTarget:(NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue {
+ NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _target = target;
+ _keyPath = [keyPath copy];
+
+ [self.leadingTerminal setNameWithFormat:@"[-initWithTarget: %@ keyPath: %@ nilValue: %@] -leadingTerminal", target, keyPath, nilValue];
+ [self.followingTerminal setNameWithFormat:@"[-initWithTarget: %@ keyPath: %@ nilValue: %@] -followingTerminal", target, keyPath, nilValue];
+
+ // Observe the key path on target for changes and forward the changes to the
+ // terminal.
+ //
+ // Intentionally capturing `self` strongly in the blocks below, so the
+ // channel object stays alive while observing.
+ RACDisposable *observationDisposable = [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change) {
+ // If the change wasn't triggered by deallocation, only affects the last
+ // path component, and ignoreNextUpdate is set, then it was triggered by
+ // this channel and should not be forwarded.
+ if (![change[RACKeyValueChangeCausedByDeallocationKey] boolValue] && [change[RACKeyValueChangeAffectedOnlyLastComponentKey] boolValue] && self.currentThreadData.ignoreNextUpdate) {
+ [self destroyCurrentThreadData];
+ return;
+ }
+
+ [self.leadingTerminal sendNext:value];
+ }];
+
+ NSString *keyPathByDeletingLastKeyPathComponent = keyPath.rac_keyPathByDeletingLastKeyPathComponent;
+ NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
+ NSUInteger keyPathComponentsCount = keyPathComponents.count;
+ NSString *lastKeyPathComponent = keyPathComponents.lastObject;
+
+ // Update the value of the property with the values received.
+ [[self.leadingTerminal
+ finally:^{
+ [observationDisposable dispose];
+ }]
+ subscribeNext:^(id x) {
+ // Check the value of the second to last key path component. Since the
+ // channel can only update the value of a property on an object, and not
+ // update intermediate objects, it can only update the value of the whole
+ // key path if this object is not nil.
+ NSObject *object = (keyPathComponentsCount > 1 ? [self.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : self.target);
+ if (object == nil) return;
+
+ // Set the ignoreNextUpdate flag before setting the value so this channel
+ // ignores the value in the subsequent -didChangeValueForKey: callback.
+ [self createCurrentThreadData];
+ self.currentThreadData.ignoreNextUpdate = YES;
+
+ [object setValue:x ?: nilValue forKey:lastKeyPathComponent];
+ } error:^(NSError *error) {
+ NSCAssert(NO, @"Received error in %@: %@", self, error);
+
+ // Log the error if we're running with assertions disabled.
+ NSLog(@"Received error in %@: %@", self, error);
+ }];
+
+ // Capture `self` weakly for the target's deallocation disposable, so we can
+ // freely deallocate if we complete before then.
+ @weakify(self);
+
+ [target.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ [self.leadingTerminal sendCompleted];
+ self.target = nil;
+ }]];
+
+ return self;
+}
+
+- (void)createCurrentThreadData {
+ NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey];
+ if (dataArray == nil) {
+ dataArray = [NSMutableArray array];
+ NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey] = dataArray;
+ [dataArray addObject:[RACKVOChannelData dataForChannel:self]];
+ return;
+ }
+
+ for (RACKVOChannelData *data in dataArray) {
+ if (data.owner == (__bridge void *)self) return;
+ }
+
+ [dataArray addObject:[RACKVOChannelData dataForChannel:self]];
+}
+
+- (void)destroyCurrentThreadData {
+ NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey];
+ NSUInteger index = [dataArray indexOfObjectPassingTest:^ BOOL (RACKVOChannelData *data, NSUInteger idx, BOOL *stop) {
+ return data.owner == (__bridge void *)self;
+ }];
+
+ if (index != NSNotFound) [dataArray removeObjectAtIndex:index];
+}
+
+@end
+
+@implementation RACKVOChannel (RACChannelTo)
+
+- (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key {
+ NSCParameterAssert(key != nil);
+
+ RACChannelTerminal *terminal = [self valueForKey:key];
+ NSCAssert([terminal isKindOfClass:RACChannelTerminal.class], @"Key \"%@\" does not identify a channel terminal", key);
+
+ return terminal;
+}
+
+- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key {
+ NSCParameterAssert(otherTerminal != nil);
+
+ RACChannelTerminal *selfTerminal = [self objectForKeyedSubscript:key];
+ [otherTerminal subscribe:selfTerminal];
+ [[selfTerminal skip:1] subscribe:otherTerminal];
+}
+
+@end
+
+@implementation RACKVOChannelData
+
++ (instancetype)dataForChannel:(RACKVOChannel *)channel {
+ RACKVOChannelData *data = [[self alloc] init];
+ data->_owner = (__bridge void *)channel;
+ return data;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.h b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.h
new file mode 100644
index 0000000..5701486
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.h
@@ -0,0 +1,31 @@
+//
+// RACKVOTrampoline.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 1/15/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "NSObject+RACKVOWrapper.h"
+#import "RACDisposable.h"
+
+// A private trampoline object that represents a KVO observation.
+//
+// Disposing of the trampoline will stop observation.
+@interface RACKVOTrampoline : RACDisposable
+
+// Initializes the receiver with the given parameters.
+//
+// target - The object whose key path should be observed. Cannot be nil.
+// observer - The object that gets notified when the value at the key path
+// changes. Can be nil.
+// keyPath - The key path on `target` to observe. Cannot be nil.
+// options - Any key value observing options to use in the observation.
+// block - The block to call when the value at the observed key path changes.
+// Cannot be nil.
+//
+// Returns the initialized object.
+- (id)initWithTarget:(NSObject *)target observer:(NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.m b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.m
new file mode 100644
index 0000000..1e4feae
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.m
@@ -0,0 +1,100 @@
+//
+// RACKVOTrampoline.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 1/15/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACKVOTrampoline.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+
+static void *RACKVOWrapperContext = &RACKVOWrapperContext;
+
+@interface RACKVOTrampoline ()
+
+// The keypath which the trampoline is observing.
+@property (nonatomic, readonly, copy) NSString *keyPath;
+
+// These properties should only be manipulated while synchronized on the
+// receiver.
+@property (nonatomic, readonly, copy) RACKVOBlock block;
+@property (nonatomic, readonly, unsafe_unretained) NSObject *target;
+@property (nonatomic, readonly, unsafe_unretained) NSObject *observer;
+
+@end
+
+@implementation RACKVOTrampoline
+
+#pragma mark Lifecycle
+
+- (id)initWithTarget:(NSObject *)target observer:(NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block {
+ NSCParameterAssert(target != nil);
+ NSCParameterAssert(keyPath != nil);
+ NSCParameterAssert(block != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _keyPath = [keyPath copy];
+
+ _block = [block copy];
+ _target = target;
+ _observer = observer;
+
+ [self.target addObserver:self forKeyPath:self.keyPath options:options context:&RACKVOWrapperContext];
+ [self.target.rac_deallocDisposable addDisposable:self];
+ [self.observer.rac_deallocDisposable addDisposable:self];
+
+ return self;
+}
+
+- (void)dealloc {
+ [self dispose];
+}
+
+#pragma mark Observation
+
+- (void)dispose {
+ NSObject *target;
+ NSObject *observer;
+
+ @synchronized (self) {
+ _block = nil;
+
+ target = self.target;
+ observer = self.observer;
+
+ _target = nil;
+ _observer = nil;
+ }
+
+ [target.rac_deallocDisposable removeDisposable:self];
+ [observer.rac_deallocDisposable removeDisposable:self];
+
+ [target removeObserver:self forKeyPath:self.keyPath context:&RACKVOWrapperContext];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (context != &RACKVOWrapperContext) {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ return;
+ }
+
+ RACKVOBlock block;
+ id observer;
+ id target;
+
+ @synchronized (self) {
+ block = self.block;
+ observer = self.observer;
+ target = self.target;
+ }
+
+ if (block == nil) return;
+
+ block(target, observer, change);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection+Private.h b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection+Private.h
new file mode 100644
index 0000000..43931f8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection+Private.h
@@ -0,0 +1,17 @@
+//
+// RACMulticastConnection+Private.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACMulticastConnection.h"
+
+@class RACSubject;
+
+@interface RACMulticastConnection ()
+
+- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h
new file mode 100644
index 0000000..5361f5d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h
@@ -0,0 +1,46 @@
+//
+// RACMulticastConnection.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+@class RACSignal;
+@class RACDisposable;
+
+/// A multicast connection encapsulates the idea of sharing one subscription to a
+/// signal to many subscribers. This is most often needed if the subscription to
+/// the underlying signal involves side-effects or shouldn't be called more than
+/// once.
+///
+/// The multicasted signal is only subscribed to when
+/// -[RACMulticastConnection connect] is called. Until that happens, no values
+/// will be sent on `signal`. See -[RACMulticastConnection autoconnect] for how
+/// -[RACMulticastConnection connect] can be called automatically.
+///
+/// Note that you shouldn't create RACMulticastConnection manually. Instead use
+/// -[RACSignal publish] or -[RACSignal multicast:].
+@interface RACMulticastConnection : NSObject
+
+/// The multicasted signal.
+@property (nonatomic, strong, readonly) RACSignal *signal;
+
+/// Connect to the underlying signal by subscribing to it. Calling this multiple
+/// times does nothing but return the existing connection's disposable.
+///
+/// Returns the disposable for the subscription to the multicasted signal.
+- (RACDisposable *)connect;
+
+/// Connects to the underlying signal when the returned signal is first
+/// subscribed to, and disposes of the subscription to the multicasted signal
+/// when the returned signal has no subscribers.
+///
+/// If new subscribers show up after being disposed, they'll subscribe and then
+/// be immediately disposed of. The returned signal will never re-connect to the
+/// multicasted signal.
+///
+/// Returns the autoconnecting signal.
+- (RACSignal *)autoconnect;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.m b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.m
new file mode 100644
index 0000000..1534d96
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.m
@@ -0,0 +1,85 @@
+//
+// RACMulticastConnection.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/11/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACMulticastConnection.h"
+#import "RACMulticastConnection+Private.h"
+#import "RACDisposable.h"
+#import "RACSerialDisposable.h"
+#import "RACSubject.h"
+#import <libkern/OSAtomic.h>
+
+@interface RACMulticastConnection () {
+ RACSubject *_signal;
+
+ // When connecting, a caller should attempt to atomically swap the value of this
+ // from `0` to `1`.
+ //
+ // If the swap is successful the caller is resposible for subscribing `_signal`
+ // to `sourceSignal` and storing the returned disposable in `serialDisposable`.
+ //
+ // If the swap is unsuccessful it means that `_sourceSignal` has already been
+ // connected and the caller has no action to take.
+ int32_t volatile _hasConnected;
+}
+
+@property (nonatomic, readonly, strong) RACSignal *sourceSignal;
+@property (strong) RACSerialDisposable *serialDisposable;
+@end
+
+@implementation RACMulticastConnection
+
+#pragma mark Lifecycle
+
+- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
+ NSCParameterAssert(source != nil);
+ NSCParameterAssert(subject != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _sourceSignal = source;
+ _serialDisposable = [[RACSerialDisposable alloc] init];
+ _signal = subject;
+
+ return self;
+}
+
+#pragma mark Connecting
+
+- (RACDisposable *)connect {
+ BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
+
+ if (shouldConnect) {
+ self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
+ }
+
+ return self.serialDisposable;
+}
+
+- (RACSignal *)autoconnect {
+ __block volatile int32_t subscriberCount = 0;
+
+ return [[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ OSAtomicIncrement32Barrier(&subscriberCount);
+
+ RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber];
+ RACDisposable *connectionDisposable = [self connect];
+
+ return [RACDisposable disposableWithBlock:^{
+ [subscriptionDisposable dispose];
+
+ if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) {
+ [connectionDisposable dispose];
+ }
+ }];
+ }]
+ setNameWithFormat:@"[%@] -autoconnect", self.signal.name];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.h b/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.h
new file mode 100644
index 0000000..fbec61c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.h
@@ -0,0 +1,17 @@
+//
+// RACObjCRuntime.h
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/19/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// A private class containing wrappers to runtime functions.
+@interface RACObjCRuntime : NSObject
+
+// Invokes objc_allocateClassPair(). Can be called from ARC code.
++ (Class)createClass:(const char *)className inheritingFromClass:(Class)superclass;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.m b/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.m
new file mode 100644
index 0000000..43cfc19
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.m
@@ -0,0 +1,22 @@
+//
+// RACObjCRuntime.m
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/19/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACObjCRuntime.h"
+#import <objc/runtime.h>
+
+#if __has_feature(objc_arc)
+#error "This file must be compiled without ARC."
+#endif
+
+@implementation RACObjCRuntime
+
++ (Class)createClass:(const char *)className inheritingFromClass:(Class)superclass {
+ return objc_allocateClassPair(superclass, className, 0);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.h b/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.h
new file mode 100644
index 0000000..9ae547b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.h
@@ -0,0 +1,29 @@
+//
+// RACPassthroughSubscriber.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "RACSubscriber.h"
+
+@class RACCompoundDisposable;
+@class RACSignal;
+
+// A private subscriber that passes through all events to another subscriber
+// while not disposed.
+@interface RACPassthroughSubscriber : NSObject <RACSubscriber>
+
+// Initializes the receiver to pass through events until disposed.
+//
+// subscriber - The subscriber to forward events to. This must not be nil.
+// signal - The signal that will be sending events to the receiver.
+// disposable - When this disposable is disposed, no more events will be
+// forwarded. This must not be nil.
+//
+// Returns an initialized passthrough subscriber.
+- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.m b/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.m
new file mode 100644
index 0000000..614d7d5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACPassthroughSubscriber.m
@@ -0,0 +1,103 @@
+//
+// RACPassthroughSubscriber.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACPassthroughSubscriber.h"
+#import "RACCompoundDisposable.h"
+#import "RACSignal.h"
+#import "RACSignalProvider.h"
+
+static const char *cleanedDTraceString(NSString *original) {
+ return [original stringByReplacingOccurrencesOfString:@"\\s+" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, original.length)].UTF8String;
+}
+
+static const char *cleanedSignalDescription(RACSignal *signal) {
+ NSString *desc = signal.description;
+
+ NSRange range = [desc rangeOfString:@" name:"];
+ if (range.location != NSNotFound) {
+ desc = [desc stringByReplacingCharactersInRange:range withString:@""];
+ }
+
+ return cleanedDTraceString(desc);
+}
+
+@interface RACPassthroughSubscriber ()
+
+// The subscriber to which events should be forwarded.
+@property (nonatomic, strong, readonly) id<RACSubscriber> innerSubscriber;
+
+// The signal sending events to this subscriber.
+//
+// This property isn't `weak` because it's only used for DTrace probes, so
+// a zeroing weak reference would incur an unnecessary performance penalty in
+// normal usage.
+@property (nonatomic, unsafe_unretained, readonly) RACSignal *signal;
+
+// A disposable representing the subscription. When disposed, no further events
+// should be sent to the `innerSubscriber`.
+@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
+
+@end
+
+@implementation RACPassthroughSubscriber
+
+#pragma mark Lifecycle
+
+- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
+ NSCParameterAssert(subscriber != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _innerSubscriber = subscriber;
+ _signal = signal;
+ _disposable = disposable;
+
+ [self.innerSubscriber didSubscribeWithDisposable:self.disposable];
+ return self;
+}
+
+#pragma mark RACSubscriber
+
+- (void)sendNext:(id)value {
+ if (self.disposable.disposed) return;
+
+ if (RACSIGNAL_NEXT_ENABLED()) {
+ RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
+ }
+
+ [self.innerSubscriber sendNext:value];
+}
+
+- (void)sendError:(NSError *)error {
+ if (self.disposable.disposed) return;
+
+ if (RACSIGNAL_ERROR_ENABLED()) {
+ RACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description));
+ }
+
+ [self.innerSubscriber sendError:error];
+}
+
+- (void)sendCompleted {
+ if (self.disposable.disposed) return;
+
+ if (RACSIGNAL_COMPLETED_ENABLED()) {
+ RACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description));
+ }
+
+ [self.innerSubscriber sendCompleted];
+}
+
+- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable {
+ if (disposable != self.disposable) {
+ [self.disposable addDisposable:disposable];
+ }
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler+Subclass.h b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler+Subclass.h
new file mode 100644
index 0000000..48ef636
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler+Subclass.h
@@ -0,0 +1,46 @@
+//
+// RACQueueScheduler+Subclass.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/6/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACQueueScheduler.h"
+
+/// An interface for use by subclasses.
+///
+/// Subclasses should use `-performAsCurrentScheduler:` to do the actual block
+/// invocation so that +[RACScheduler currentScheduler] behaves as expected.
+///
+/// **Note that RACSchedulers are expected to be serial**. Subclasses must honor
+/// that contract. See `RACTargetQueueScheduler` for a queue-based scheduler
+/// which will enforce the serialization guarantee.
+@interface RACQueueScheduler ()
+
+/// The queue on which blocks are enqueued.
+@property (nonatomic, readonly) dispatch_queue_t queue;
+
+/// Initializes the receiver with the name of the scheduler and the queue which
+/// the scheduler should use.
+///
+/// name - The name of the scheduler. If nil, a default name will be used.
+/// queue - The queue upon which the receiver should enqueue scheduled blocks.
+/// This argument must not be NULL.
+///
+/// Returns the initialized object.
+- (id)initWithName:(NSString *)name queue:(dispatch_queue_t)queue;
+
+/// Performs the given block with the receiver as the current scheduler for
+/// `queue`. This should only be called by subclasses to perform scheduled blocks
+/// on their queue.
+///
+/// block - The block to execute. Cannot be NULL.
+- (void)performAsCurrentScheduler:(void (^)(void))block;
+
+/// Converts a date into a GCD time using dispatch_walltime().
+///
+/// date - The date to convert. This must not be nil.
++ (dispatch_time_t)wallTimeWithDate:(NSDate *)date;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.h
new file mode 100644
index 0000000..ef42512
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.h
@@ -0,0 +1,18 @@
+//
+// RACQueueScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+
+/// An abstract scheduler which asynchronously enqueues all its work to a Grand
+/// Central Dispatch queue.
+///
+/// Because RACQueueScheduler is abstract, it should not be instantiated
+/// directly. Create a subclass using the `RACQueueScheduler+Subclass.h`
+/// interface and use that instead.
+@interface RACQueueScheduler : RACScheduler
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.m
new file mode 100644
index 0000000..7ae8202
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.m
@@ -0,0 +1,120 @@
+//
+// RACQueueScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACQueueScheduler.h"
+#import "RACBacktrace.h"
+#import "RACDisposable.h"
+#import "RACQueueScheduler+Subclass.h"
+#import "RACScheduler+Private.h"
+
+@implementation RACQueueScheduler
+
+#pragma mark Lifecycle
+
+- (void)dealloc {
+ dispatch_release(_queue);
+}
+
+- (id)initWithName:(NSString *)name queue:(dispatch_queue_t)queue {
+ NSCParameterAssert(queue != NULL);
+
+ self = [super initWithName:name];
+ if (self == nil) return nil;
+
+ dispatch_retain(queue);
+ _queue = queue;
+
+ return self;
+}
+
+#pragma mark Date Conversions
+
++ (dispatch_time_t)wallTimeWithDate:(NSDate *)date {
+ NSCParameterAssert(date != nil);
+
+ double seconds = 0;
+ double frac = modf(date.timeIntervalSince1970, &seconds);
+
+ struct timespec walltime = {
+ .tv_sec = (time_t)fmin(fmax(seconds, LONG_MIN), LONG_MAX),
+ .tv_nsec = (long)fmin(fmax(frac * NSEC_PER_SEC, LONG_MIN), LONG_MAX)
+ };
+
+ return dispatch_walltime(&walltime, 0);
+}
+
+#pragma mark RACScheduler
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ RACDisposable *disposable = [[RACDisposable alloc] init];
+
+ dispatch_async(self.queue, ^{
+ if (disposable.disposed) return;
+ [self performAsCurrentScheduler:block];
+ });
+
+ return disposable;
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(block != NULL);
+
+ RACDisposable *disposable = [[RACDisposable alloc] init];
+
+ dispatch_after([self.class wallTimeWithDate:date], self.queue, ^{
+ if (disposable.disposed) return;
+ [self performAsCurrentScheduler:block];
+ });
+
+ return disposable;
+}
+
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(interval > 0.0 && interval < INT64_MAX / NSEC_PER_SEC);
+ NSCParameterAssert(leeway >= 0.0 && leeway < INT64_MAX / NSEC_PER_SEC);
+ NSCParameterAssert(block != NULL);
+
+ uint64_t intervalInNanoSecs = (uint64_t)(interval * NSEC_PER_SEC);
+ uint64_t leewayInNanoSecs = (uint64_t)(leeway * NSEC_PER_SEC);
+
+ dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
+ dispatch_source_set_timer(timer, [self.class wallTimeWithDate:date], intervalInNanoSecs, leewayInNanoSecs);
+ dispatch_source_set_event_handler(timer, block);
+ dispatch_resume(timer);
+
+ return [RACDisposable disposableWithBlock:^{
+ dispatch_source_cancel(timer);
+ dispatch_release(timer);
+ }];
+}
+
+- (void)performAsCurrentScheduler:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ // If we're using a concurrent queue, we could end up in here concurrently,
+ // in which case we *don't* want to clear the current scheduler immediately
+ // after our block is done executing, but only *after* all our concurrent
+ // invocations are done.
+
+ RACScheduler *previousScheduler = RACScheduler.currentScheduler;
+ NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self;
+
+ block();
+
+ if (previousScheduler != nil) {
+ NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler;
+ } else {
+ [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey];
+ }
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.h b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.h
new file mode 100644
index 0000000..cd6f2ec
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.h
@@ -0,0 +1,23 @@
+//
+// RACReplaySubject.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/14/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubject.h"
+
+extern const NSUInteger RACReplaySubjectUnlimitedCapacity;
+
+
+/// A replay subject saves the values it is sent (up to its defined capacity)
+/// and resends those to new subscribers. It will also replay an error or
+/// completion.
+@interface RACReplaySubject : RACSubject
+
+/// Creates a new replay subject with the given capacity. A capacity of
+/// RACReplaySubjectUnlimitedCapacity means values are never trimmed.
++ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m
new file mode 100644
index 0000000..bca008e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m
@@ -0,0 +1,112 @@
+//
+// RACReplaySubject.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/14/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACReplaySubject.h"
+#import "RACDisposable.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriber.h"
+#import "RACTuple.h"
+#import "RACCompoundDisposable.h"
+
+const NSUInteger RACReplaySubjectUnlimitedCapacity = NSUIntegerMax;
+
+@interface RACReplaySubject ()
+
+@property (nonatomic, assign, readonly) NSUInteger capacity;
+
+// These properties should only be modified while synchronized on self.
+@property (nonatomic, strong, readonly) NSMutableArray *valuesReceived;
+@property (nonatomic, assign) BOOL hasCompleted;
+@property (nonatomic, assign) BOOL hasError;
+@property (nonatomic, strong) NSError *error;
+
+@end
+
+
+@implementation RACReplaySubject
+
+#pragma mark Lifecycle
+
++ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity {
+ return [[self alloc] initWithCapacity:capacity];
+}
+
+- (instancetype)init {
+ return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity];
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)capacity {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _capacity = capacity;
+ _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);
+
+ return self;
+}
+
+#pragma mark RACSignal
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
+ @synchronized (self) {
+ for (id value in self.valuesReceived) {
+ if (compoundDisposable.disposed) return;
+
+ [subscriber sendNext:([value isKindOfClass:RACTupleNil.class] ? nil : value)];
+ }
+
+ if (compoundDisposable.disposed) return;
+
+ if (self.hasCompleted) {
+ [subscriber sendCompleted];
+ } else if (self.hasError) {
+ [subscriber sendError:self.error];
+ } else {
+ RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
+ [compoundDisposable addDisposable:subscriptionDisposable];
+ }
+ }
+ }];
+
+ [compoundDisposable addDisposable:schedulingDisposable];
+
+ return compoundDisposable;
+}
+
+#pragma mark RACSubscriber
+
+- (void)sendNext:(id)value {
+ @synchronized (self) {
+ [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
+ [super sendNext:value];
+
+ if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
+ [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
+ }
+ }
+}
+
+- (void)sendCompleted {
+ @synchronized (self) {
+ self.hasCompleted = YES;
+ [super sendCompleted];
+ }
+}
+
+- (void)sendError:(NSError *)e {
+ @synchronized (self) {
+ self.hasError = YES;
+ self.error = e;
+ [super sendError:e];
+ }
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.h
new file mode 100644
index 0000000..73e5674
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.h
@@ -0,0 +1,17 @@
+//
+// RACReturnSignal.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+
+// A private `RACSignal` subclasses that synchronously sends a value to any
+// subscribers, then completes.
+@interface RACReturnSignal : RACSignal
+
++ (RACSignal *)return:(id)value;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.m
new file mode 100644
index 0000000..3006634
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACReturnSignal.m
@@ -0,0 +1,90 @@
+//
+// RACReturnSignal.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-10.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACReturnSignal.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriber.h"
+#import "RACUnit.h"
+
+@interface RACReturnSignal ()
+
+// The value to send upon subscription.
+@property (nonatomic, strong, readonly) id value;
+
+@end
+
+@implementation RACReturnSignal
+
+#pragma mark Properties
+
+// Only allow this signal's name to be customized in DEBUG, since it's
+// potentially a singleton in release builds (see +return:).
+- (void)setName:(NSString *)name {
+#ifdef DEBUG
+ [super setName:name];
+#endif
+}
+
+- (NSString *)name {
+#ifdef DEBUG
+ return super.name;
+#else
+ return @"+return:";
+#endif
+}
+
+#pragma mark Lifecycle
+
++ (RACSignal *)return:(id)value {
+#ifndef DEBUG
+ // In release builds, use singletons for two very common cases.
+ if (value == RACUnit.defaultUnit) {
+ static RACReturnSignal *unitSingleton;
+ static dispatch_once_t unitPred;
+
+ dispatch_once(&unitPred, ^{
+ unitSingleton = [[self alloc] init];
+ unitSingleton->_value = RACUnit.defaultUnit;
+ });
+
+ return unitSingleton;
+ } else if (value == nil) {
+ static RACReturnSignal *nilSingleton;
+ static dispatch_once_t nilPred;
+
+ dispatch_once(&nilPred, ^{
+ nilSingleton = [[self alloc] init];
+ nilSingleton->_value = nil;
+ });
+
+ return nilSingleton;
+ }
+#endif
+
+ RACReturnSignal *signal = [[self alloc] init];
+ signal->_value = value;
+
+#ifdef DEBUG
+ [signal setNameWithFormat:@"+return: %@", value];
+#endif
+
+ return signal;
+}
+
+#pragma mark Subscription
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCParameterAssert(subscriber != nil);
+
+ return [RACScheduler.subscriptionScheduler schedule:^{
+ [subscriber sendNext:self.value];
+ [subscriber sendCompleted];
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler+Private.h b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler+Private.h
new file mode 100644
index 0000000..2c91e66
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler+Private.h
@@ -0,0 +1,34 @@
+//
+// RACScheduler+Private.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/29/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+
+// The thread-specific current scheduler key.
+extern NSString * const RACSchedulerCurrentSchedulerKey;
+
+// A private interface for internal RAC use only.
+@interface RACScheduler ()
+
+// A dedicated scheduler that fills two requirements:
+//
+// 1. By the time subscription happens, we need a valid +currentScheduler.
+// 2. Subscription should happen as soon as possible.
+//
+// To fulfill those two, if we already have a valid +currentScheduler, it
+// immediately executes scheduled blocks. If we don't, it will execute scheduled
+// blocks with a private background scheduler.
++ (instancetype)subscriptionScheduler;
+
+// Initializes the receiver with the given name.
+//
+// name - The name of the scheduler. If nil, a default name will be used.
+//
+// Returns the initialized object.
+- (id)initWithName:(NSString *)name;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h
new file mode 100644
index 0000000..ce1ee32
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.h
@@ -0,0 +1,148 @@
+//
+// RACScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/// The priority for the scheduler.
+///
+/// RACSchedulerPriorityHigh - High priority.
+/// RACSchedulerPriorityDefault - Default priority.
+/// RACSchedulerPriorityLow - Low priority.
+/// RACSchedulerPriorityBackground - Background priority.
+typedef enum : long {
+ RACSchedulerPriorityHigh = DISPATCH_QUEUE_PRIORITY_HIGH,
+ RACSchedulerPriorityDefault = DISPATCH_QUEUE_PRIORITY_DEFAULT,
+ RACSchedulerPriorityLow = DISPATCH_QUEUE_PRIORITY_LOW,
+ RACSchedulerPriorityBackground = DISPATCH_QUEUE_PRIORITY_BACKGROUND,
+} RACSchedulerPriority;
+
+/// Scheduled with -scheduleRecursiveBlock:, this type of block is passed a block
+/// with which it can call itself recursively.
+typedef void (^RACSchedulerRecursiveBlock)(void (^reschedule)(void));
+
+@class RACDisposable;
+
+/// Schedulers are used to control when and where work is performed.
+@interface RACScheduler : NSObject
+
+/// A singleton scheduler that immediately executes the blocks it is given.
+///
+/// **Note:** Unlike most other schedulers, this does not set the current
+/// scheduler. There may still be a valid +currentScheduler if this is used
+/// within a block scheduled on a different scheduler.
++ (RACScheduler *)immediateScheduler;
+
+/// A singleton scheduler that executes blocks in the main thread.
++ (RACScheduler *)mainThreadScheduler;
+
+/// Creates and returns a new background scheduler with the given priority and
+/// name. The name is for debug and instrumentation purposes only.
+///
+/// Scheduler creation is cheap. It's unnecessary to save the result of this
+/// method call unless you want to serialize some actions on the same background
+/// scheduler.
++ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name;
+
+/// Invokes +schedulerWithPriority:name: with a default name.
++ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;
+
+/// Invokes +schedulerWithPriority: with RACSchedulerPriorityDefault.
++ (RACScheduler *)scheduler;
+
+/// The current scheduler. This will only be valid when used from within a
+/// -[RACScheduler schedule:] block or when on the main thread.
++ (RACScheduler *)currentScheduler;
+
+/// Schedule the given block for execution on the scheduler.
+///
+/// Scheduled blocks will be executed in the order in which they were scheduled.
+///
+/// block - The block to schedule for execution. Cannot be nil.
+///
+/// Returns a disposable which can be used to cancel the scheduled block before
+/// it begins executing, or nil if cancellation is not supported.
+- (RACDisposable *)schedule:(void (^)(void))block;
+
+/// Schedule the given block for execution on the scheduler at or after
+/// a specific time.
+///
+/// Note that blocks scheduled for a certain time will not preempt any other
+/// scheduled work that is executing at the time.
+///
+/// When invoked on the +immediateScheduler, the calling thread **will block**
+/// until the specified time.
+///
+/// date - The earliest time at which `block` should begin executing. The block
+/// may not execute immediately at this time, whether due to system load
+/// or another block on the scheduler currently being run. Cannot be nil.
+/// block - The block to schedule for execution. Cannot be nil.
+///
+/// Returns a disposable which can be used to cancel the scheduled block before
+/// it begins executing, or nil if cancellation is not supported.
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block;
+
+/// Schedule the given block for execution on the scheduler after the delay.
+///
+/// Converts the delay into an NSDate, then invokes `-after:schedule:`.
+- (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block;
+
+/// Reschedule the given block at a particular interval, starting at a specific
+/// time, and with a given leeway for deferral.
+///
+/// Note that blocks scheduled for a certain time will not preempt any other
+/// scheduled work that is executing at the time.
+///
+/// Regardless of the value of `leeway`, the given block may not execute exactly
+/// at `when` or exactly on successive intervals, whether due to system load or
+/// because another block is currently being run on the scheduler.
+///
+/// It is considered undefined behavior to invoke this method on the
+/// +immediateScheduler.
+///
+/// date - The earliest time at which `block` should begin executing. The
+/// block may not execute immediately at this time, whether due to
+/// system load or another block on the scheduler currently being
+/// run. Cannot be nil.
+/// interval - The interval at which the block should be rescheduled, starting
+/// from `date`. This will use the system wall clock, to avoid
+/// skew when the computer goes to sleep.
+/// leeway - A hint to the system indicating the number of seconds that each
+/// scheduling can be deferred. Note that this is just a hint, and
+/// there may be some additional latency no matter what.
+/// block - The block to repeatedly schedule for execution. Cannot be nil.
+///
+/// Returns a disposable which can be used to cancel the automatic scheduling and
+/// rescheduling, or nil if cancellation is not supported.
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block;
+
+/// Schedule the given recursive block for execution on the scheduler. The
+/// scheduler will automatically flatten any recursive scheduling into iteration
+/// instead, so this can be used without issue for blocks that may keep invoking
+/// themselves forever.
+///
+/// Scheduled blocks will be executed in the order in which they were scheduled.
+///
+/// recursiveBlock - The block to schedule for execution. When invoked, the
+/// recursive block will be passed a `void (^)(void)` block
+/// which will reschedule the recursive block at the end of the
+/// receiver's queue. This passed-in block will automatically
+/// skip scheduling if the scheduling of the `recursiveBlock`
+/// was disposed in the meantime.
+///
+/// Returns a disposable which can be used to cancel the scheduled block before
+/// it begins executing, or to stop it from rescheduling if it's already begun
+/// execution.
+- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock;
+
+@end
+
+@interface RACScheduler (Deprecated)
+
++ (RACScheduler *)schedulerWithQueue:(dispatch_queue_t)queue name:(NSString *)name __attribute__((deprecated("Use -[RACScheduler initWithName:targetQueue:] instead.")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.m
new file mode 100644
index 0000000..c829896
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACScheduler.m
@@ -0,0 +1,206 @@
+//
+// RACScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/16/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+#import "RACBacktrace.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACImmediateScheduler.h"
+#import "RACScheduler+Private.h"
+#import "RACSubscriptionScheduler.h"
+#import "RACTargetQueueScheduler.h"
+
+// The key for the thread-specific current scheduler.
+NSString * const RACSchedulerCurrentSchedulerKey = @"RACSchedulerCurrentSchedulerKey";
+
+@interface RACScheduler ()
+@property (nonatomic, readonly, copy) NSString *name;
+@end
+
+@implementation RACScheduler
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.name];
+}
+
+#pragma mark Initializers
+
+- (id)initWithName:(NSString *)name {
+ self = [super init];
+ if (self == nil) return nil;
+
+ if (name == nil) {
+ _name = [NSString stringWithFormat:@"com.ReactiveCocoa.%@.anonymousScheduler", self.class];
+ } else {
+ _name = [name copy];
+ }
+
+ return self;
+}
+
+#pragma mark Schedulers
+
++ (instancetype)immediateScheduler {
+ static dispatch_once_t onceToken;
+ static RACScheduler *immediateScheduler;
+ dispatch_once(&onceToken, ^{
+ immediateScheduler = [[RACImmediateScheduler alloc] init];
+ });
+
+ return immediateScheduler;
+}
+
++ (instancetype)mainThreadScheduler {
+ static dispatch_once_t onceToken;
+ static RACScheduler *mainThreadScheduler;
+ dispatch_once(&onceToken, ^{
+ mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"com.ReactiveCocoa.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()];
+ });
+
+ return mainThreadScheduler;
+}
+
++ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
+ return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
+}
+
++ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority {
+ return [self schedulerWithPriority:priority name:@"com.ReactiveCocoa.RACScheduler.backgroundScheduler"];
+}
+
++ (instancetype)scheduler {
+ return [self schedulerWithPriority:RACSchedulerPriorityDefault];
+}
+
++ (instancetype)subscriptionScheduler {
+ static dispatch_once_t onceToken;
+ static RACScheduler *subscriptionScheduler;
+ dispatch_once(&onceToken, ^{
+ subscriptionScheduler = [[RACSubscriptionScheduler alloc] init];
+ });
+
+ return subscriptionScheduler;
+}
+
++ (BOOL)isOnMainThread {
+ return [NSOperationQueue.currentQueue isEqual:NSOperationQueue.mainQueue] || [NSThread isMainThread];
+}
+
++ (instancetype)currentScheduler {
+ RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
+ if (scheduler != nil) return scheduler;
+ if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;
+
+ return nil;
+}
+
+#pragma mark Scheduling
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
+ return nil;
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
+ return nil;
+}
+
+- (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block {
+ return [self after:[NSDate dateWithTimeIntervalSinceNow:delay] schedule:block];
+}
+
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
+ NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
+ return nil;
+}
+
+- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ [self scheduleRecursiveBlock:[recursiveBlock copy] addingToDisposable:disposable];
+ return disposable;
+}
+
+- (void)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock addingToDisposable:(RACCompoundDisposable *)disposable {
+ @autoreleasepool {
+ RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
+ [disposable addDisposable:selfDisposable];
+
+ __weak RACDisposable *weakSelfDisposable = selfDisposable;
+
+ RACDisposable *schedulingDisposable = [self schedule:^{
+ @autoreleasepool {
+ // At this point, we've been invoked, so our disposable is now useless.
+ [disposable removeDisposable:weakSelfDisposable];
+ }
+
+ if (disposable.disposed) return;
+
+ void (^reallyReschedule)(void) = ^{
+ if (disposable.disposed) return;
+ [self scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable];
+ };
+
+ // Protects the variables below.
+ //
+ // This doesn't actually need to be __block qualified, but Clang
+ // complains otherwise. :C
+ __block NSLock *lock = [[NSLock alloc] init];
+ lock.name = [NSString stringWithFormat:@"%@ %s", self, sel_getName(_cmd)];
+
+ __block NSUInteger rescheduleCount = 0;
+
+ // Set to YES once synchronous execution has finished. Further
+ // rescheduling should occur immediately (rather than being
+ // flattened).
+ __block BOOL rescheduleImmediately = NO;
+
+ @autoreleasepool {
+ recursiveBlock(^{
+ [lock lock];
+ BOOL immediate = rescheduleImmediately;
+ if (!immediate) ++rescheduleCount;
+ [lock unlock];
+
+ if (immediate) reallyReschedule();
+ });
+ }
+
+ [lock lock];
+ NSUInteger synchronousCount = rescheduleCount;
+ rescheduleImmediately = YES;
+ [lock unlock];
+
+ for (NSUInteger i = 0; i < synchronousCount; i++) {
+ reallyReschedule();
+ }
+ }];
+
+ [selfDisposable addDisposable:schedulingDisposable];
+ }
+}
+
+@end
+
+@implementation RACScheduler (Deprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
++ (instancetype)schedulerWithQueue:(dispatch_queue_t)queue name:(NSString *)name {
+ NSCParameterAssert(queue != NULL);
+
+ return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:queue];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.h b/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.h
new file mode 100644
index 0000000..03da6a2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.h
@@ -0,0 +1,19 @@
+//
+// RACScopedDisposable.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDisposable.h"
+
+
+/// A disposable that calls its own -dispose when it is dealloc'd.
+@interface RACScopedDisposable : RACDisposable
+
+/// Creates a new scoped disposable that will also dispose of the given
+/// disposable when it is dealloc'd.
++ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.m b/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.m
new file mode 100644
index 0000000..91115be
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.m
@@ -0,0 +1,32 @@
+//
+// RACScopedDisposable.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScopedDisposable.h"
+
+@implementation RACScopedDisposable
+
+#pragma mark Lifecycle
+
++ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable {
+ return [self disposableWithBlock:^{
+ [disposable dispose];
+ }];
+}
+
+- (void)dealloc {
+ [self dispose];
+}
+
+#pragma mark RACDisposable
+
+- (RACScopedDisposable *)asScopedDisposable {
+ // totally already are
+ return self;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
new file mode 100644
index 0000000..a39f840
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
@@ -0,0 +1,154 @@
+//
+// RACSequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "RACStream.h"
+
+@class RACScheduler;
+@class RACSignal;
+
+/// Represents an immutable sequence of values. Unless otherwise specified, the
+/// sequences' values are evaluated lazily on demand. Like Cocoa collections,
+/// sequences cannot contain nil.
+///
+/// Most inherited RACStream methods that accept a block will execute the block
+/// _at most_ once for each value that is evaluated in the returned sequence.
+/// Side effects are subject to the behavior described in
+/// +sequenceWithHeadBlock:tailBlock:.
+///
+/// Implemented as a class cluster. A minimal implementation for a subclass
+/// consists simply of -head and -tail.
+@interface RACSequence : RACStream <NSCoding, NSCopying, NSFastEnumeration>
+
+/// The first object in the sequence, or nil if the sequence is empty.
+///
+/// Subclasses must provide an implementation of this method.
+@property (nonatomic, strong, readonly) id head;
+
+/// All but the first object in the sequence, or nil if the sequence is empty.
+///
+/// Subclasses must provide an implementation of this method.
+@property (nonatomic, strong, readonly) RACSequence *tail;
+
+/// Evaluates the full sequence to produce an equivalently-sized array.
+@property (nonatomic, copy, readonly) NSArray *array;
+
+/// Returns an enumerator of all objects in the sequence.
+@property (nonatomic, copy, readonly) NSEnumerator *objectEnumerator;
+
+/// Converts a sequence into an eager sequence.
+///
+/// An eager sequence fully evaluates all of its values immediately. Sequences
+/// derived from an eager sequence will also be eager.
+///
+/// Returns a new eager sequence, or the receiver if the sequence is already
+/// eager.
+@property (nonatomic, copy, readonly) RACSequence *eagerSequence;
+
+/// Converts a sequence into a lazy sequence.
+///
+/// A lazy sequence evaluates its values on demand, as they are accessed.
+/// Sequences derived from a lazy sequence will also be lazy.
+///
+/// Returns a new lazy sequence, or the receiver if the sequence is already lazy.
+@property (nonatomic, copy, readonly) RACSequence *lazySequence;
+
+/// Invokes -signalWithScheduler: with a new RACScheduler.
+- (RACSignal *)signal;
+
+/// Evaluates the full sequence on the given scheduler.
+///
+/// Each item is evaluated in its own scheduled block, such that control of the
+/// scheduler is yielded between each value.
+///
+/// Returns a signal which sends the receiver's values on the given scheduler as
+/// they're evaluated.
+- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler;
+
+/// Applies a left fold to the sequence.
+///
+/// This is the same as iterating the sequence along with a provided start value.
+/// This uses a constant amount of memory. A left fold is left-associative so in
+/// the sequence [1,2,3] the block would applied in the following order:
+/// reduce(reduce(reduce(start, 1), 2), 3)
+///
+/// start - The starting value for the fold. Used as `accumulator` for the
+/// first fold.
+/// reduce - The block used to combine the accumulated value and the next value.
+/// Cannot be nil.
+///
+/// Returns a reduced value.
+- (id)foldLeftWithStart:(id)start reduce:(id (^)(id accumulator, id value))reduce;
+
+/// Applies a right fold to the sequence.
+///
+/// A right fold is equivalent to recursion on the list. The block is evaluated
+/// from the right to the left in list. It is right associative so it's applied
+/// to the rightmost elements first. For example, in the sequence [1,2,3] the
+/// block is applied in the order:
+/// reduce(1, reduce(2, reduce(3, start)))
+///
+/// start - The starting value for the fold.
+/// reduce - The block used to combine the accumulated value and the next head.
+/// The block is given the accumulated value and the value of the rest
+/// of the computation (result of the recursion). This is computed when
+/// you retrieve its value using `rest.head`. This allows you to
+/// prevent unnecessary computation by not accessing `rest.head` if you
+/// don't need to.
+///
+/// Returns a reduced value.
+- (id)foldRightWithStart:(id)start reduce:(id (^)(id first, RACSequence *rest))reduce;
+
+/// Check if any value in sequence passes the block.
+///
+/// block - The block predicate used to check each item. Cannot be nil.
+///
+/// Returns a boolean indiciating if any value in the sequence passed.
+- (BOOL)any:(BOOL (^)(id value))block;
+
+/// Check if all values in the sequence pass the block.
+///
+/// block - The block predicate used to check each item. Cannot be nil.
+///
+/// Returns a boolean indicating if all values in the sequence passed.
+- (BOOL)all:(BOOL (^)(id value))block;
+
+/// Returns the first object that passes the block.
+///
+/// block - The block predicate used to check each item. Cannot be nil.
+///
+/// Returns an object that passes the block or nil if no objects passed.
+- (id)objectPassingTest:(BOOL (^)(id value))block;
+
+/// Creates a sequence that dynamically generates its values.
+///
+/// headBlock - Invoked the first time -head is accessed.
+/// tailBlock - Invoked the first time -tail is accessed.
+///
+/// The results from each block are memoized, so each block will be invoked at
+/// most once, no matter how many times the head and tail properties of the
+/// sequence are accessed.
+///
+/// Any side effects in `headBlock` or `tailBlock` should be thread-safe, since
+/// the sequence may be evaluated at any time from any thread. Not only that, but
+/// -tail may be accessed before -head, or both may be accessed simultaneously.
+/// As noted above, side effects will only be triggered the _first_ time -head or
+/// -tail is invoked.
+///
+/// Returns a sequence that lazily invokes the given blocks to provide head and
+/// tail. `headBlock` must not be nil.
++ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock;
+
+@end
+
+@interface RACSequence (Deprecated)
+
+- (id)foldLeftWithStart:(id)start combine:(id (^)(id accumulator, id value))combine __attribute__((deprecated("Renamed to -foldLeftWithStart:reduce:")));
+- (id)foldRightWithStart:(id)start combine:(id (^)(id first, RACSequence *rest))combine __attribute__((deprecated("Renamed to -foldRightWithStart:reduce:")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.m
new file mode 100644
index 0000000..9567ea8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.m
@@ -0,0 +1,384 @@
+//
+// RACSequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACSequence.h"
+#import "RACArraySequence.h"
+#import "RACDynamicSequence.h"
+#import "RACEagerSequence.h"
+#import "RACEmptySequence.h"
+#import "RACScheduler.h"
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+#import "RACTuple.h"
+#import "RACUnarySequence.h"
+
+// An enumerator over sequences.
+@interface RACSequenceEnumerator : NSEnumerator
+
+// The sequence the enumerator is enumerating.
+//
+// This will change as the enumerator is exhausted. This property should only be
+// accessed while synchronized on self.
+@property (nonatomic, strong) RACSequence *sequence;
+
+@end
+
+@interface RACSequence ()
+
+// Performs one iteration of lazy binding, passing through values from `current`
+// until the sequence is exhausted, then recursively binding the remaining
+// values in the receiver.
+//
+// Returns a new sequence which contains `current`, followed by the combined
+// result of all applications of `block` to the remaining values in the receiver.
+- (instancetype)bind:(RACStreamBindBlock)block passingThroughValuesFromSequence:(RACSequence *)current;
+
+@end
+
+@implementation RACSequenceEnumerator
+
+- (id)nextObject {
+ id object = nil;
+
+ @synchronized (self) {
+ object = self.sequence.head;
+ self.sequence = self.sequence.tail;
+ }
+
+ return object;
+}
+
+@end
+
+@implementation RACSequence
+
+#pragma mark Lifecycle
+
++ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock {
+ return [[RACDynamicSequence sequenceWithHeadBlock:headBlock tailBlock:tailBlock] setNameWithFormat:@"+sequenceWithHeadBlock:tailBlock:"];
+}
+
+#pragma mark Class cluster primitives
+
+- (id)head {
+ NSCAssert(NO, @"%s must be overridden by subclasses", __func__);
+ return nil;
+}
+
+- (RACSequence *)tail {
+ NSCAssert(NO, @"%s must be overridden by subclasses", __func__);
+ return nil;
+}
+
+#pragma mark RACStream
+
++ (instancetype)empty {
+ return RACEmptySequence.empty;
+}
+
++ (instancetype)return:(id)value {
+ return [RACUnarySequence return:value];
+}
+
+- (instancetype)bind:(RACStreamBindBlock (^)(void))block {
+ RACStreamBindBlock bindBlock = block();
+ return [[self bind:bindBlock passingThroughValuesFromSequence:nil] setNameWithFormat:@"[%@] -bind:", self.name];
+}
+
+- (instancetype)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence {
+ // Store values calculated in the dependency here instead, avoiding any kind
+ // of temporary collection and boxing.
+ //
+ // This relies on the implementation of RACDynamicSequence synchronizing
+ // access to its head, tail, and dependency, and we're only doing it because
+ // we really need the performance.
+ __block RACSequence *valuesSeq = self;
+ __block RACSequence *current = passthroughSequence;
+ __block BOOL stop = NO;
+
+ RACSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^ id {
+ while (current.head == nil) {
+ if (stop) return nil;
+
+ // We've exhausted the current sequence, create a sequence from the
+ // next value.
+ id value = valuesSeq.head;
+
+ if (value == nil) {
+ // We've exhausted all the sequences.
+ stop = YES;
+ return nil;
+ }
+
+ current = (id)bindBlock(value, &stop);
+ if (current == nil) {
+ stop = YES;
+ return nil;
+ }
+
+ valuesSeq = valuesSeq.tail;
+ }
+
+ NSCAssert([current isKindOfClass:RACSequence.class], @"-bind: block returned an object that is not a sequence: %@", current);
+ return nil;
+ } headBlock:^(id _) {
+ return current.head;
+ } tailBlock:^ id (id _) {
+ if (stop) return nil;
+
+ return [valuesSeq bind:bindBlock passingThroughValuesFromSequence:current.tail];
+ }];
+
+ sequence.name = self.name;
+ return sequence;
+}
+
+- (instancetype)concat:(RACStream *)stream {
+ NSCParameterAssert(stream != nil);
+
+ return [[[RACArraySequence sequenceWithArray:@[ self, stream ] offset:0]
+ flatten]
+ setNameWithFormat:@"[%@] -concat: %@", self.name, stream];
+}
+
+- (instancetype)zipWith:(RACSequence *)sequence {
+ NSCParameterAssert(sequence != nil);
+
+ return [[RACSequence
+ sequenceWithHeadBlock:^ id {
+ if (self.head == nil || sequence.head == nil) return nil;
+ return RACTuplePack(self.head, sequence.head);
+ } tailBlock:^ id {
+ if (self.tail == nil || [[RACSequence empty] isEqual:self.tail]) return nil;
+ if (sequence.tail == nil || [[RACSequence empty] isEqual:sequence.tail]) return nil;
+
+ return [self.tail zipWith:sequence.tail];
+ }]
+ setNameWithFormat:@"[%@] -zipWith: %@", self.name, sequence];
+}
+
+#pragma mark Extended methods
+
+- (NSArray *)array {
+ NSMutableArray *array = [NSMutableArray array];
+ for (id obj in self) {
+ [array addObject:obj];
+ }
+
+ return [array copy];
+}
+
+- (NSEnumerator *)objectEnumerator {
+ RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
+ enumerator.sequence = self;
+ return enumerator;
+}
+
+- (RACSignal *)signal {
+ return [[self signalWithScheduler:[RACScheduler scheduler]] setNameWithFormat:@"[%@] -signal", self.name];
+}
+
+- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block RACSequence *sequence = self;
+
+ return [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) {
+ if (sequence.head == nil) {
+ [subscriber sendCompleted];
+ return;
+ }
+
+ [subscriber sendNext:sequence.head];
+
+ sequence = sequence.tail;
+ reschedule();
+ }];
+ }] setNameWithFormat:@"[%@] -signalWithScheduler:", self.name];
+}
+
+- (id)foldLeftWithStart:(id)start reduce:(id (^)(id, id))reduce {
+ NSCParameterAssert(reduce != NULL);
+
+ if (self.head == nil) return start;
+
+ for (id value in self) {
+ start = reduce(start, value);
+ }
+
+ return start;
+}
+
+- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *))reduce {
+ NSCParameterAssert(reduce != NULL);
+
+ if (self.head == nil) return start;
+
+ RACSequence *rest = [RACSequence sequenceWithHeadBlock:^{
+ return [self.tail foldRightWithStart:start reduce:reduce];
+ } tailBlock:nil];
+
+ return reduce(self.head, rest);
+}
+
+- (BOOL)any:(BOOL (^)(id))block {
+ NSCParameterAssert(block != NULL);
+
+ return [self objectPassingTest:block] != nil;
+}
+
+- (BOOL)all:(BOOL (^)(id))block {
+ NSCParameterAssert(block != NULL);
+
+ NSNumber *result = [self foldLeftWithStart:@YES reduce:^(NSNumber *accumulator, id value) {
+ return @(accumulator.boolValue && block(value));
+ }];
+
+ return result.boolValue;
+}
+
+- (id)objectPassingTest:(BOOL (^)(id))block {
+ NSCParameterAssert(block != NULL);
+
+ return [self filter:block].head;
+}
+
+- (RACSequence *)eagerSequence {
+ return [RACEagerSequence sequenceWithArray:self.array offset:0];
+}
+
+- (RACSequence *)lazySequence {
+ return self;
+}
+
+#pragma mark NSCopying
+
+- (id)copyWithZone:(NSZone *)zone {
+ return self;
+}
+
+#pragma mark NSCoding
+
+- (Class)classForCoder {
+ // Most sequences should be archived as RACArraySequences.
+ return RACArraySequence.class;
+}
+
+- (id)initWithCoder:(NSCoder *)coder {
+ if (![self isKindOfClass:RACArraySequence.class]) return [[RACArraySequence alloc] initWithCoder:coder];
+
+ // Decoding is handled in RACArraySequence.
+ return [super init];
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:self.array forKey:@"array"];
+}
+
+#pragma mark NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {
+ if (state->state == ULONG_MAX) {
+ // Enumeration has completed.
+ return 0;
+ }
+
+ // We need to traverse the sequence itself on repeated calls to this
+ // method, so use the 'state' field to track the current head.
+ RACSequence *(^getSequence)(void) = ^{
+ return (__bridge RACSequence *)(void *)state->state;
+ };
+
+ void (^setSequence)(RACSequence *) = ^(RACSequence *sequence) {
+ // Release the old sequence and retain the new one.
+ CFBridgingRelease((void *)state->state);
+
+ state->state = (unsigned long)CFBridgingRetain(sequence);
+ };
+
+ void (^complete)(void) = ^{
+ // Release any stored sequence.
+ setSequence(nil);
+ state->state = ULONG_MAX;
+ };
+
+ if (state->state == 0) {
+ // Since a sequence doesn't mutate, this just needs to be set to
+ // something non-NULL.
+ state->mutationsPtr = state->extra;
+
+ setSequence(self);
+ }
+
+ state->itemsPtr = stackbuf;
+
+ NSUInteger enumeratedCount = 0;
+ while (enumeratedCount < len) {
+ RACSequence *seq = getSequence();
+
+ // Because the objects in a sequence may be generated lazily, we want to
+ // prevent them from being released until the enumerator's used them.
+ __autoreleasing id obj = seq.head;
+ if (obj == nil) {
+ complete();
+ break;
+ }
+
+ stackbuf[enumeratedCount++] = obj;
+
+ if (seq.tail == nil) {
+ complete();
+ break;
+ }
+
+ setSequence(seq.tail);
+ }
+
+ return enumeratedCount;
+}
+
+#pragma mark NSObject
+
+- (NSUInteger)hash {
+ return [self.head hash];
+}
+
+- (BOOL)isEqual:(RACSequence *)seq {
+ if (self == seq) return YES;
+ if (![seq isKindOfClass:RACSequence.class]) return NO;
+
+ for (id<NSObject> selfObj in self) {
+ id<NSObject> seqObj = seq.head;
+
+ // Handles the nil case too.
+ if (![seqObj isEqual:selfObj]) return NO;
+
+ seq = seq.tail;
+ }
+
+ // self is now depleted -- the argument should be too.
+ return (seq.head == nil);
+}
+
+@end
+
+@implementation RACSequence (Deprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (id)foldLeftWithStart:(id)start combine:(id (^)(id accumulator, id value))combine {
+ return [self foldLeftWithStart:start reduce:combine];
+}
+
+- (id)foldRightWithStart:(id)start combine:(id (^)(id first, RACSequence *rest))combine {
+ return [self foldRightWithStart:start reduce:combine];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.h
new file mode 100644
index 0000000..a3fc1d4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.h
@@ -0,0 +1,43 @@
+//
+// RACSerialDisposable.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDisposable.h"
+
+/// A disposable that contains exactly one other disposable and allows it to be
+/// swapped out atomically.
+@interface RACSerialDisposable : RACDisposable
+
+/// The inner disposable managed by the serial disposable.
+///
+/// This property is thread-safe for reading and writing. However, if you want to
+/// read the current value _and_ write a new one atomically, use
+/// -swapInDisposable: instead.
+///
+/// Disposing of the receiver will also dispose of the current disposable set for
+/// this property, then set the property to nil. If any new disposable is set
+/// after the receiver is disposed, it will be disposed immediately and this
+/// property will remain set to nil.
+@property (atomic, strong) RACDisposable *disposable;
+
+/// Creates a serial disposable which will wrap the given disposable.
+///
+/// disposable - The value to set for `disposable`. This may be nil.
+///
+/// Returns a RACSerialDisposable, or nil if an error occurs.
++ (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable;
+
+/// Atomically swaps the receiver's `disposable` for `newDisposable`.
+///
+/// newDisposable - The new value for `disposable`. If the receiver has already
+/// been disposed, this disposable will be too, and `disposable`
+/// will remain set to nil. This argument may be nil.
+///
+/// Returns the previous value for the `disposable` property.
+- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.m
new file mode 100644
index 0000000..975afbf
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSerialDisposable.m
@@ -0,0 +1,133 @@
+//
+// RACSerialDisposable.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSerialDisposable.h"
+#import <libkern/OSAtomic.h>
+
+@interface RACSerialDisposable () {
+ // A reference to the receiver's `disposable`. This variable must only be
+ // modified atomically.
+ //
+ // If this is `self`, no `disposable` has been set, but the receiver has not
+ // been disposed of yet. `self` is never stored retained.
+ //
+ // If this is `nil`, the receiver has been disposed.
+ //
+ // Otherwise, this is a retained reference to the inner disposable and the
+ // receiver has not been disposed of yet.
+ void * volatile _disposablePtr;
+}
+
+@end
+
+@implementation RACSerialDisposable
+
+#pragma mark Properties
+
+- (BOOL)isDisposed {
+ return _disposablePtr == nil;
+}
+
+- (RACDisposable *)disposable {
+ RACDisposable *disposable = (__bridge id)_disposablePtr;
+ return (disposable == self ? nil : disposable);
+}
+
+- (void)setDisposable:(RACDisposable *)disposable {
+ [self swapInDisposable:disposable];
+}
+
+#pragma mark Lifecycle
+
++ (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable {
+ RACSerialDisposable *serialDisposable = [[self alloc] init];
+ serialDisposable.disposable = disposable;
+ return serialDisposable;
+}
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _disposablePtr = (__bridge void *)self;
+ OSMemoryBarrier();
+
+ return self;
+}
+
+- (id)initWithBlock:(void (^)(void))block {
+ self = [self init];
+ if (self == nil) return nil;
+
+ self.disposable = [RACDisposable disposableWithBlock:block];
+
+ return self;
+}
+
+- (void)dealloc {
+ self.disposable = nil;
+}
+
+#pragma mark Inner Disposable
+
+- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable {
+ void * const selfPtr = (__bridge void *)self;
+
+ // Only retain the new disposable if it's not `self`.
+ // Take ownership before attempting the swap so that a subsequent swap
+ // receives an owned reference.
+ void *newDisposablePtr = selfPtr;
+ if (newDisposable != nil) {
+ newDisposablePtr = (void *)CFBridgingRetain(newDisposable);
+ }
+
+ void *existingDisposablePtr;
+ // Keep trying while we're not disposed.
+ while ((existingDisposablePtr = _disposablePtr) != NULL) {
+ if (!OSAtomicCompareAndSwapPtrBarrier(existingDisposablePtr, newDisposablePtr, &_disposablePtr)) {
+ continue;
+ }
+
+ // Return nil if _disposablePtr was set to self. Otherwise, release
+ // the old value and return it as an object.
+ if (existingDisposablePtr == selfPtr) {
+ return nil;
+ } else {
+ return CFBridgingRelease(existingDisposablePtr);
+ }
+ }
+
+ // At this point, we've found out that we were already disposed.
+ [newDisposable dispose];
+
+ // Failed to swap, clean up the ownership we took prior to the swap.
+ if (newDisposable != nil) {
+ CFRelease(newDisposablePtr);
+ }
+
+ return nil;
+}
+
+#pragma mark Disposal
+
+- (void)dispose {
+ void *existingDisposablePtr;
+
+ while ((existingDisposablePtr = _disposablePtr) != NULL) {
+ if (OSAtomicCompareAndSwapPtrBarrier(existingDisposablePtr, NULL, &_disposablePtr)) {
+ if (existingDisposablePtr != (__bridge void *)self) {
+ RACDisposable *existingDisposable = CFBridgingRelease(existingDisposablePtr);
+ [existingDisposable dispose];
+ }
+
+ break;
+ }
+ }
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
new file mode 100644
index 0000000..53b10d6
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
@@ -0,0 +1,625 @@
+//
+// RACSignal+Operations.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-09-06.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "RACSignal.h"
+
+/// The domain for errors originating in RACSignal operations.
+extern NSString * const RACSignalErrorDomain;
+
+/// The error code used with -timeout:.
+extern const NSInteger RACSignalErrorTimedOut;
+
+/// The error code used when a value passed into +switch:cases:default: does not
+/// match any of the cases, and no default was given.
+extern const NSInteger RACSignalErrorNoMatchingCase;
+
+@class RACMulticastConnection;
+@class RACDisposable;
+@class RACScheduler;
+@class RACSequence;
+@class RACSubject;
+@class RACTuple;
+@class RACCommand;
+@protocol RACSubscriber;
+
+@interface RACSignal (Operations)
+
+/// Do the given block on `next`. This should be used to inject side effects into
+/// the signal.
+- (RACSignal *)doNext:(void (^)(id x))block;
+
+/// Do the given block on `error`. This should be used to inject side effects
+/// into the signal.
+- (RACSignal *)doError:(void (^)(NSError *error))block;
+
+/// Do the given block on `completed`. This should be used to inject side effects
+/// into the signal.
+- (RACSignal *)doCompleted:(void (^)(void))block;
+
+/// Send `next`s only if we don't receive another `next` in `interval` seconds.
+///
+/// If a `next` is received, and then another `next` is received before
+/// `interval` seconds have passed, the first value is discarded.
+///
+/// After `interval` seconds have passed since the most recent `next` was sent,
+/// the most recent `next` is forwarded on the scheduler that the value was
+/// originally received on. If +[RACScheduler currentScheduler] was nil at the
+/// time, a private background scheduler is used.
+///
+/// Returns a signal which sends throttled and delayed `next` events. Completion
+/// and errors are always forwarded immediately.
+- (RACSignal *)throttle:(NSTimeInterval)interval;
+
+/// Throttles `next`s for which `predicate` returns YES.
+///
+/// When `predicate` returns YES for a `next`:
+///
+/// 1. If another `next` is received before `interval` seconds have passed, the
+/// prior value is discarded. This happens regardless of whether the new
+/// value will be throttled.
+/// 2. After `interval` seconds have passed since the value was originally
+/// received, it will be forwarded on the scheduler that it was received
+/// upon. If +[RACScheduler currentScheduler] was nil at the time, a private
+/// background scheduler is used.
+///
+/// When `predicate` returns NO for a `next`, it is forwarded immediately,
+/// without any throttling.
+///
+/// interval - The number of seconds for which to buffer the latest value that
+/// passes `predicate`.
+/// predicate - Passed each `next` from the receiver, this block returns
+/// whether the given value should be throttled. This argument must
+/// not be nil.
+///
+/// Returns a signal which sends `next` events, throttled when `predicate`
+/// returns YES. Completion and errors are always forwarded immediately.
+- (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id next))predicate;
+
+/// Forwards `next` and `completed` events after delaying for `interval` seconds
+/// on the current scheduler (on which the events were delivered).
+///
+/// If +[RACScheduler currentScheduler] is nil when `next` or `completed` is
+/// received, a private background scheduler is used.
+///
+/// Returns a signal which sends delayed `next` and `completed` events. Errors
+/// are always forwarded immediately.
+- (RACSignal *)delay:(NSTimeInterval)interval;
+
+/// Resubscribes when the signal completes.
+- (RACSignal *)repeat;
+
+/// Execute the given block each time a subscription is created.
+///
+/// block - A block which defines the subscription side effects. Cannot be `nil`.
+///
+/// Example:
+///
+/// // Write new file, with backup.
+/// [[[[fileManager
+/// rac_createFileAtPath:path contents:data]
+/// initially:^{
+/// // 2. Second, backup current file
+/// [fileManager moveItemAtPath:path toPath:backupPath error:nil];
+/// }]
+/// initially:^{
+/// // 1. First, acquire write lock.
+/// [writeLock lock];
+/// }]
+/// finally:^{
+/// [writeLock unlock];
+/// }];
+///
+/// Returns a signal that passes through all events of the receiver, plus
+/// introduces side effects which occur prior to any subscription side effects
+/// of the receiver.
+- (RACSignal *)initially:(void (^)(void))block;
+
+/// Execute the given block when the signal completes or errors.
+- (RACSignal *)finally:(void (^)(void))block;
+
+/// Divides the receiver's `next`s into buffers which deliver every `interval`
+/// seconds.
+///
+/// interval - The interval in which values are grouped into one buffer.
+/// scheduler - The scheduler upon which the returned signal will deliver its
+/// values. This must not be nil or +[RACScheduler
+/// immediateScheduler].
+///
+/// Returns a signal which sends RACTuples of the buffered values at each
+/// interval on `scheduler`. When the receiver completes, any currently-buffered
+/// values will be sent immediately.
+- (RACSignal *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
+
+/// Collect all receiver's `next`s into a NSArray. nil values will be converted
+/// to NSNull.
+///
+/// This corresponds to the `ToArray` method in Rx.
+///
+/// Returns a signal which sends a single NSArray when the receiver completes
+/// successfully.
+- (RACSignal *)collect;
+
+/// Takes the last `count` `next`s after the receiving signal completes.
+- (RACSignal *)takeLast:(NSUInteger)count;
+
+/// Combines the latest values from the receiver and the given signal into
+/// RACTuples, once both have sent at least one `next`.
+///
+/// Any additional `next`s will result in a new RACTuple with the latest values
+/// from both signals.
+///
+/// signal - The signal to combine with. This argument must not be nil.
+///
+/// Returns a signal which sends RACTuples of the combined values, forwards any
+/// `error` events, and completes when both input signals complete.
+- (RACSignal *)combineLatestWith:(RACSignal *)signal;
+
+/// Combines the latest values from the given signals into RACTuples, once all
+/// the signals have sent at least one `next`.
+///
+/// Any additional `next`s will result in a new RACTuple with the latest values
+/// from all signals.
+///
+/// signals - The signals to combine. If this collection is empty, the returned
+/// signal will immediately complete upon subscription.
+///
+/// Returns a signal which sends RACTuples of the combined values, forwards any
+/// `error` events, and completes when all input signals complete.
++ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals;
+
+/// Combines signals using +combineLatest:, then reduces the resulting tuples
+/// into a single value using -reduceEach:.
+///
+/// signals - The signals to combine. If this collection is empty, the
+/// returned signal will immediately complete upon subscription.
+/// reduceBlock - The block which reduces the latest values from all the
+/// signals into one value. It must take as many arguments as the
+/// number of signals given. Each argument will be an object
+/// argument. The return value must be an object. This argument
+/// must not be nil.
+///
+/// Example:
+///
+/// [RACSignal combineLatest:@[ stringSignal, intSignal ] reduce:^(NSString *string, NSNumber *number) {
+/// return [NSString stringWithFormat:@"%@: %@", string, number];
+/// }];
+///
+/// Returns a signal which sends the results from each invocation of
+/// `reduceBlock`.
++ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock;
+
+/// Merges the receiver and the given signal with `+merge:` and returns the
+/// resulting signal.
+- (RACSignal *)merge:(RACSignal *)signal;
+
+/// Sends the latest `next` from any of the signals.
+///
+/// Returns a signal that passes through values from each of the given signals,
+/// and sends `completed` when all of them complete. If any signal sends an error,
+/// the returned signal sends `error` immediately.
++ (RACSignal *)merge:(id<NSFastEnumeration>)signals;
+
+/// Merges the signals sent by the receiver into a flattened signal, but only
+/// subscribes to `maxConcurrent` number of signals at a time. New signals are
+/// queued and subscribed to as other signals complete.
+///
+/// If an error occurs on any of the signals, it is sent on the returned signal.
+/// It completes only after the receiver and all sent signals have completed.
+///
+/// This corresponds to `Merge<TSource>(IObservable<IObservable<TSource>>, Int32)`
+/// in Rx.
+///
+/// maxConcurrent - the maximum number of signals to subscribe to at a
+/// time. If 0, it subscribes to an unlimited number of
+/// signals.
+- (RACSignal *)flatten:(NSUInteger)maxConcurrent;
+
+/// Ignores all `next`s from the receiver, waits for the receiver to complete,
+/// then subscribes to a new signal.
+///
+/// block - A block which will create or obtain a new signal to subscribe to,
+/// executed only after the receiver completes. This block must not be
+/// nil, and it must not return a nil signal.
+///
+/// Returns a signal which will pass through the events of the signal created in
+/// `block`. If the receiver errors out, the returned signal will error as well.
+- (RACSignal *)then:(RACSignal * (^)(void))block;
+
+/// Concats the inner signals of a signal of signals.
+- (RACSignal *)concat;
+
+/// Aggregate `next`s with the given start and combination.
+- (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock;
+
+/// Aggregate `next`s with the given start and combination. The start factory
+/// block is called to get a new start object for each subscription.
+- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock;
+
+/// Invokes -setKeyPath:onObject:nilValue: with `nil` for the nil value.
+///
+/// WARNING: Under certain conditions, this method is known to be thread-unsafe.
+/// See the description in -setKeyPath:onObject:nilValue:.
+- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object;
+
+/// Binds the receiver to an object, automatically setting the given key path on
+/// every `next`. When the signal completes, the binding is automatically
+/// disposed of.
+///
+/// WARNING: Under certain conditions, this method is known to be thread-unsafe.
+/// A crash can result if `object` is deallocated concurrently on
+/// another thread within a window of time between a value being sent
+/// on this signal and immediately prior to the invocation of
+/// -setValue:forKeyPath:, which sets the property. To prevent this,
+/// ensure `object` is deallocated on the same thread the receiver
+/// sends on, or ensure that the returned disposable is disposed of
+/// before `object` deallocates.
+/// See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/1184
+///
+/// Sending an error on the signal is considered undefined behavior, and will
+/// generate an assertion failure in Debug builds.
+///
+/// A given key on an object should only have one active signal bound to it at any
+/// given time. Binding more than one signal to the same property is considered
+/// undefined behavior.
+///
+/// keyPath - The key path to update with `next`s from the receiver.
+/// object - The object that `keyPath` is relative to.
+/// nilValue - The value to set at the key path whenever `nil` is sent by the
+/// receiver. This may be nil when binding to object properties, but
+/// an NSValue should be used for primitive properties, to avoid an
+/// exception if `nil` is sent (which might occur if an intermediate
+/// object is set to `nil`).
+///
+/// Returns a disposable which can be used to terminate the binding.
+- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(id)nilValue;
+
+/// Sends NSDate.date every `interval` seconds.
+///
+/// interval - The time interval in seconds at which the current time is sent.
+/// scheduler - The scheduler upon which the current NSDate should be sent. This
+/// must not be nil or +[RACScheduler immediateScheduler].
+///
+/// Returns a signal that sends the current date/time every `interval` on
+/// `scheduler`.
++ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
+
+/// Sends NSDate.date at intervals of at least `interval` seconds, up to
+/// approximately `interval` + `leeway` seconds.
+///
+/// The created signal will defer sending each `next` for at least `interval`
+/// seconds, and for an additional amount of time up to `leeway` seconds in the
+/// interest of performance or power consumption. Note that some additional
+/// latency is to be expected, even when specifying a `leeway` of 0.
+///
+/// interval - The base interval between `next`s.
+/// scheduler - The scheduler upon which the current NSDate should be sent. This
+/// must not be nil or +[RACScheduler immediateScheduler].
+/// leeway - The maximum amount of additional time the `next` can be deferred.
+///
+/// Returns a signal that sends the current date/time at intervals of at least
+/// `interval seconds` up to approximately `interval` + `leeway` seconds on
+/// `scheduler`.
++ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway;
+
+/// Take `next`s until the `signalTrigger` sends `next` or `completed`.
+///
+/// Returns a signal which passes through all events from the receiver until
+/// `signalTrigger` sends `next` or `completed`, at which point the returned signal
+/// will send `completed`.
+- (RACSignal *)takeUntil:(RACSignal *)signalTrigger;
+
+/// Take `next`s until the `replacement` sends an event.
+///
+/// replacement - The signal which replaces the receiver as soon as it sends an
+/// event.
+///
+/// Returns a signal which passes through `next`s and `error` from the receiver
+/// until `replacement` sends an event, at which point the returned signal will
+/// send that event and switch to passing through events from `replacement`
+/// instead, regardless of whether the receiver has sent events already.
+- (RACSignal *)takeUntilReplacement:(RACSignal *)replacement;
+
+/// Subscribe to the returned signal when an error occurs.
+- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock;
+
+/// Subscribe to the given signal when an error occurs.
+- (RACSignal *)catchTo:(RACSignal *)signal;
+
+/// Runs `tryBlock` against each of the receiver's values, passing values
+/// until `tryBlock` returns NO, or the receiver completes.
+///
+/// tryBlock - An action to run against each of the receiver's values.
+/// The block should return YES to indicate that the action was
+/// successful. This block must not be nil.
+///
+/// Example:
+///
+/// // The returned signal will send an error if data values cannot be
+/// // written to `someFileURL`.
+/// [signal try:^(NSData *data, NSError **errorPtr) {
+/// return [data writeToURL:someFileURL options:NSDataWritingAtomic error:errorPtr];
+/// }];
+///
+/// Returns a signal which passes through all the values of the receiver. If
+/// `tryBlock` fails for any value, the returned signal will error using the
+/// `NSError` passed out from the block.
+- (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock;
+
+/// Runs `mapBlock` against each of the receiver's values, mapping values until
+/// `mapBlock` returns nil, or the receiver completes.
+///
+/// mapBlock - An action to map each of the receiver's values. The block should
+/// return a non-nil value to indicate that the action was successful.
+/// This block must not be nil.
+///
+/// Example:
+///
+/// // The returned signal will send an error if data cannot be read from
+/// // `fileURL`.
+/// [signal tryMap:^(NSURL *fileURL, NSError **errorPtr) {
+/// return [NSData dataWithContentsOfURL:fileURL options:0 error:errorPtr];
+/// }];
+///
+/// Returns a signal which transforms all the values of the receiver. If
+/// `mapBlock` returns nil for any value, the returned signal will error using
+/// the `NSError` passed out from the block.
+- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock;
+
+/// Returns the first `next`. Note that this is a blocking call.
+- (id)first;
+
+/// Returns the first `next` or `defaultValue` if the signal completes or errors
+/// without sending a `next`. Note that this is a blocking call.
+- (id)firstOrDefault:(id)defaultValue;
+
+/// Returns the first `next` or `defaultValue` if the signal completes or errors
+/// without sending a `next`. If an error occurs success will be NO and error
+/// will be populated. Note that this is a blocking call.
+///
+/// Both success and error may be NULL.
+- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error;
+
+/// Blocks the caller and waits for the signal to complete.
+///
+/// error - If not NULL, set to any error that occurs.
+///
+/// Returns whether the signal completed successfully. If NO, `error` will be set
+/// to the error that occurred.
+- (BOOL)waitUntilCompleted:(NSError **)error;
+
+/// Defer creation of a signal until the signal's actually subscribed to.
+///
+/// This can be used to effectively turn a hot signal into a cold signal.
++ (RACSignal *)defer:(RACSignal * (^)(void))block;
+
+/// Every time the receiver sends a new RACSignal, subscribes and sends `next`s and
+/// `error`s only for that signal.
+///
+/// The receiver must be a signal of signals.
+///
+/// Returns a signal which passes through `next`s and `error`s from the latest
+/// signal sent by the receiver, and sends `completed` when both the receiver and
+/// the last sent signal complete.
+- (RACSignal *)switchToLatest;
+
+/// Switches between the signals in `cases` as well as `defaultSignal` based on
+/// the latest value sent by `signal`.
+///
+/// signal - A signal of objects used as keys in the `cases` dictionary.
+/// This argument must not be nil.
+/// cases - A dictionary that has signals as values. This argument must
+/// not be nil. A RACTupleNil key in this dictionary will match
+/// nil `next` events that are received on `signal`.
+/// defaultSignal - The signal to pass through after `signal` sends a value for
+/// which `cases` does not contain a signal. If nil, any
+/// unmatched values will result in
+/// a RACSignalErrorNoMatchingCase error.
+///
+/// Returns a signal which passes through `next`s and `error`s from one of the
+/// the signals in `cases` or `defaultSignal`, and sends `completed` when both
+/// `signal` and the last used signal complete. If no `defaultSignal` is given,
+/// an unmatched `next` will result in an error on the returned signal.
++ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(RACSignal *)defaultSignal;
+
+/// Switches between `trueSignal` and `falseSignal` based on the latest value
+/// sent by `boolSignal`.
+///
+/// boolSignal - A signal of BOOLs determining whether `trueSignal` or
+/// `falseSignal` should be active. This argument must not be nil.
+/// trueSignal - The signal to pass through after `boolSignal` has sent YES.
+/// This argument must not be nil.
+/// falseSignal - The signal to pass through after `boolSignal` has sent NO. This
+/// argument must not be nil.
+///
+/// Returns a signal which passes through `next`s and `error`s from `trueSignal`
+/// and/or `falseSignal`, and sends `completed` when both `boolSignal` and the
+/// last switched signal complete.
++ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal;
+
+/// Add every `next` to an array. Nils are represented by NSNulls. Note that this
+/// is a blocking call.
+///
+/// **This is not the same as the `ToArray` method in Rx.** See -collect for
+/// that behavior instead.
+///
+/// Returns the array of `next` values, or nil if an error occurs.
+- (NSArray *)toArray;
+
+/// Add every `next` to a sequence. Nils are represented by NSNulls.
+///
+/// This corresponds to the `ToEnumerable` method in Rx.
+///
+/// Returns a sequence which provides values from the signal as they're sent.
+/// Trying to retrieve a value from the sequence which has not yet been sent will
+/// block.
+@property (nonatomic, strong, readonly) RACSequence *sequence;
+
+/// Creates and returns a multicast connection. This allows you to share a single
+/// subscription to the underlying signal.
+- (RACMulticastConnection *)publish;
+
+/// Creates and returns a multicast connection that pushes values into the given
+/// subject. This allows you to share a single subscription to the underlying
+/// signal.
+- (RACMulticastConnection *)multicast:(RACSubject *)subject;
+
+/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
+/// immediately connects to the resulting RACMulticastConnection.
+///
+/// Returns the connected, multicasted signal.
+- (RACSignal *)replay;
+
+/// Multicasts the signal to a RACReplaySubject of capacity 1, and immediately
+/// connects to the resulting RACMulticastConnection.
+///
+/// Returns the connected, multicasted signal.
+- (RACSignal *)replayLast;
+
+/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
+/// lazily connects to the resulting RACMulticastConnection.
+///
+/// This means the returned signal will subscribe to the multicasted signal only
+/// when the former receives its first subscription.
+///
+/// Returns the lazily connected, multicasted signal.
+- (RACSignal *)replayLazily;
+
+/// Sends an error after `interval` seconds if the source doesn't complete
+/// before then.
+///
+/// The error will be in the RACSignalErrorDomain and have a code of
+/// RACSignalErrorTimedOut.
+///
+/// interval - The number of seconds after which the signal should error out.
+/// scheduler - The scheduler upon which any timeout error should be sent. This
+/// must not be nil or +[RACScheduler immediateScheduler].
+///
+/// Returns a signal that passes through the receiver's events, until the stream
+/// finishes or times out, at which point an error will be sent on `scheduler`.
+- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
+
+/// Creates and returns a signal that delivers its events on the given scheduler.
+/// Any side effects of the receiver will still be performed on the original
+/// thread.
+///
+/// This is ideal when the signal already performs its work on the desired
+/// thread, but you want to handle its events elsewhere.
+///
+/// This corresponds to the `ObserveOn` method in Rx.
+- (RACSignal *)deliverOn:(RACScheduler *)scheduler;
+
+/// Creates and returns a signal that executes its side effects and delivers its
+/// events on the given scheduler.
+///
+/// Use of this operator should be avoided whenever possible, because the
+/// receiver's side effects may not be safe to run on another thread. If you just
+/// want to receive the signal's events on `scheduler`, use -deliverOn: instead.
+- (RACSignal *)subscribeOn:(RACScheduler *)scheduler;
+
+/// Groups each received object into a group, as determined by calling `keyBlock`
+/// with that object. The object sent is transformed by calling `transformBlock`
+/// with the object. If `transformBlock` is nil, it sends the original object.
+///
+/// The returned signal is a signal of RACGroupedSignal.
+- (RACSignal *)groupBy:(id<NSCopying> (^)(id object))keyBlock transform:(id (^)(id object))transformBlock;
+
+/// Calls -[RACSignal groupBy:keyBlock transform:nil].
+- (RACSignal *)groupBy:(id<NSCopying> (^)(id object))keyBlock;
+
+/// Sends an [NSNumber numberWithBool:YES] if the receiving signal sends any
+/// objects.
+- (RACSignal *)any;
+
+/// Sends an [NSNumber numberWithBool:YES] if the receiving signal sends any
+/// objects that pass `predicateBlock`.
+///
+/// predicateBlock - cannot be nil.
+- (RACSignal *)any:(BOOL (^)(id object))predicateBlock;
+
+/// Sends an [NSNumber numberWithBool:YES] if all the objects the receiving
+/// signal sends pass `predicateBlock`.
+///
+/// predicateBlock - cannot be nil.
+- (RACSignal *)all:(BOOL (^)(id object))predicateBlock;
+
+/// Resubscribes to the receiving signal if an error occurs, up until it has
+/// retried the given number of times.
+///
+/// retryCount - if 0, it keeps retrying until it completes.
+- (RACSignal *)retry:(NSInteger)retryCount;
+
+/// Resubscribes to the receiving signal if an error occurs.
+- (RACSignal *)retry;
+
+/// Sends the latest value from the receiver only when `sampler` sends a value.
+/// The returned signal could repeat values if `sampler` fires more often than
+/// the receiver. Values from `sampler` are ignored before the receiver sends
+/// its first value.
+///
+/// sampler - The signal that controls when the latest value from the receiver
+/// is sent. Cannot be nil.
+- (RACSignal *)sample:(RACSignal *)sampler;
+
+/// Ignores all `next`s from the receiver.
+///
+/// Returns a signal which only passes through `error` or `completed` events from
+/// the receiver.
+- (RACSignal *)ignoreValues;
+
+/// Converts each of the receiver's events into a RACEvent object.
+///
+/// Returns a signal which sends the receiver's events as RACEvents, and
+/// completes after the receiver sends `completed` or `error`.
+- (RACSignal *)materialize;
+
+/// Converts each RACEvent in the receiver back into "real" RACSignal events.
+///
+/// Returns a signal which sends `next` for each value RACEvent, `error` for each
+/// error RACEvent, and `completed` for each completed RACEvent.
+- (RACSignal *)dematerialize;
+
+/// Inverts each NSNumber-wrapped BOOL sent by the receiver. It will assert if
+/// the receiver sends anything other than NSNumbers.
+///
+/// Returns a signal of inverted NSNumber-wrapped BOOLs.
+- (RACSignal *)not;
+
+/// Performs a boolean AND on all of the RACTuple of NSNumbers in sent by the receiver.
+///
+/// Asserts if the receiver sends anything other than a RACTuple of one or more NSNumbers.
+///
+/// Returns a signal that applies AND to each NSNumber in the tuple.
+- (RACSignal *)and;
+
+/// Performs a boolean OR on all of the RACTuple of NSNumbers in sent by the receiver.
+///
+/// Asserts if the receiver sends anything other than a RACTuple of one or more NSNumbers.
+///
+/// Returns a signal that applies OR to each NSNumber in the tuple.
+- (RACSignal *)or;
+
+@end
+
+@interface RACSignal (OperationsDeprecated)
+
+- (RACSignal *)windowWithStart:(RACSignal *)openSignal close:(RACSignal * (^)(RACSignal *start))closeBlock __attribute__((deprecated("See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/587")));
+- (RACSignal *)buffer:(NSUInteger)bufferCount __attribute__((deprecated("See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/587")));
+- (RACSignal *)let:(RACSignal * (^)(RACSignal *sharedSignal))letBlock __attribute__((deprecated("Use -publish instead")));
++ (RACSignal *)interval:(NSTimeInterval)interval __attribute__((deprecated("Use +interval:onScheduler: instead")));
++ (RACSignal *)interval:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway __attribute__((deprecated("Use +interval:onScheduler:withLeeway: instead")));
+- (RACSignal *)bufferWithTime:(NSTimeInterval)interval __attribute__((deprecated("Use -bufferWithTime:onScheduler: instead")));
+- (RACSignal *)timeout:(NSTimeInterval)interval __attribute__((deprecated("Use -timeout:onScheduler: instead")));
+- (RACDisposable *)toProperty:(NSString *)keyPath onObject:(NSObject *)object __attribute__((deprecated("Renamed to -setKeyPath:onObject:")));
+- (RACSignal *)ignoreElements __attribute__((deprecated("Renamed to -ignoreValues")));
+- (RACSignal *)sequenceNext:(RACSignal * (^)(void))block __attribute__((deprecated("Renamed to -then:")));
+- (RACSignal *)aggregateWithStart:(id)start combine:(id (^)(id running, id next))combineBlock __attribute__((deprecated("Renamed to -aggregateWithStart:reduce:")));
+- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory combine:(id (^)(id running, id next))combineBlock __attribute__((deprecated("Renamed to -aggregateWithStartFactory:reduce:")));
+- (RACDisposable *)executeCommand:(RACCommand *)command __attribute__((deprecated("Use -flattenMap: or -subscribeNext: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.m
new file mode 100644
index 0000000..6e36dd6
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.m
@@ -0,0 +1,1436 @@
+//
+// RACSignal+Operations.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-09-06.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal+Operations.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACEvent.h"
+#import "RACGroupedSignal.h"
+#import "RACMulticastConnection+Private.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler+Private.h"
+#import "RACScheduler.h"
+#import "RACSerialDisposable.h"
+#import "RACSignalSequence.h"
+#import "RACStream+Private.h"
+#import "RACSubject.h"
+#import "RACSubscriber+Private.h"
+#import "RACSubscriber.h"
+#import "RACTuple.h"
+#import "RACUnit.h"
+#import <libkern/OSAtomic.h>
+#import <objc/runtime.h>
+
+NSString * const RACSignalErrorDomain = @"RACSignalErrorDomain";
+
+const NSInteger RACSignalErrorTimedOut = 1;
+const NSInteger RACSignalErrorNoMatchingCase = 2;
+
+// Subscribes to the given signal with the given blocks.
+//
+// If the signal errors or completes, the corresponding block is invoked. If the
+// disposable passed to the block is _not_ disposed, then the signal is
+// subscribed to again.
+static RACDisposable *subscribeForever (RACSignal *signal, void (^next)(id), void (^error)(NSError *, RACDisposable *), void (^completed)(RACDisposable *)) {
+ next = [next copy];
+ error = [error copy];
+ completed = [completed copy];
+
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ RACSchedulerRecursiveBlock recursiveBlock = ^(void (^recurse)(void)) {
+ RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
+ [compoundDisposable addDisposable:selfDisposable];
+
+ __weak RACDisposable *weakSelfDisposable = selfDisposable;
+
+ RACDisposable *subscriptionDisposable = [signal subscribeNext:next error:^(NSError *e) {
+ @autoreleasepool {
+ error(e, compoundDisposable);
+ [compoundDisposable removeDisposable:weakSelfDisposable];
+ }
+
+ recurse();
+ } completed:^{
+ @autoreleasepool {
+ completed(compoundDisposable);
+ [compoundDisposable removeDisposable:weakSelfDisposable];
+ }
+
+ recurse();
+ }];
+
+ [selfDisposable addDisposable:subscriptionDisposable];
+ };
+
+ // Subscribe once immediately, and then use recursive scheduling for any
+ // further resubscriptions.
+ recursiveBlock(^{
+ RACScheduler *recursiveScheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler];
+
+ RACDisposable *schedulingDisposable = [recursiveScheduler scheduleRecursiveBlock:recursiveBlock];
+ [compoundDisposable addDisposable:schedulingDisposable];
+ });
+
+ return compoundDisposable;
+}
+
+@implementation RACSignal (Operations)
+
+- (RACSignal *)doNext:(void (^)(id x))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [self subscribeNext:^(id x) {
+ block(x);
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -doNext:", self.name];
+}
+
+- (RACSignal *)doError:(void (^)(NSError *error))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ block(error);
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -doError:", self.name];
+}
+
+- (RACSignal *)doCompleted:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ block();
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -doCompleted:", self.name];
+}
+
+- (RACSignal *)throttle:(NSTimeInterval)interval {
+ return [[self throttle:interval valuesPassingTest:^(id _) {
+ return YES;
+ }] setNameWithFormat:@"[%@] -throttle: %f", self.name, (double)interval];
+}
+
+- (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id next))predicate {
+ NSCParameterAssert(interval >= 0);
+ NSCParameterAssert(predicate != nil);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ // We may never use this scheduler, but we need to set it up ahead of
+ // time so that our scheduled blocks are run serially if we do.
+ RACScheduler *scheduler = [RACScheduler scheduler];
+
+ // Information about any currently-buffered `next` event.
+ __block id nextValue = nil;
+ __block BOOL hasNextValue = NO;
+ RACSerialDisposable *nextDisposable = [[RACSerialDisposable alloc] init];
+
+ void (^flushNext)(BOOL send) = ^(BOOL send) {
+ @synchronized (compoundDisposable) {
+ [nextDisposable.disposable dispose];
+
+ if (!hasNextValue) return;
+ if (send) [subscriber sendNext:nextValue];
+
+ nextValue = nil;
+ hasNextValue = NO;
+ }
+ };
+
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ RACScheduler *delayScheduler = RACScheduler.currentScheduler ?: scheduler;
+ BOOL shouldThrottle = predicate(x);
+
+ @synchronized (compoundDisposable) {
+ flushNext(NO);
+ if (!shouldThrottle) {
+ [subscriber sendNext:x];
+ return;
+ }
+
+ nextValue = x;
+ hasNextValue = YES;
+ nextDisposable.disposable = [delayScheduler afterDelay:interval schedule:^{
+ flushNext(YES);
+ }];
+ }
+ } error:^(NSError *error) {
+ [compoundDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ flushNext(YES);
+ [subscriber sendCompleted];
+ }];
+
+ [compoundDisposable addDisposable:subscriptionDisposable];
+ return compoundDisposable;
+ }] setNameWithFormat:@"[%@] -throttle: %f valuesPassingTest:", self.name, (double)interval];
+}
+
+- (RACSignal *)delay:(NSTimeInterval)interval {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ // We may never use this scheduler, but we need to set it up ahead of
+ // time so that our scheduled blocks are run serially if we do.
+ RACScheduler *scheduler = [RACScheduler scheduler];
+
+ void (^schedule)(dispatch_block_t) = ^(dispatch_block_t block) {
+ RACScheduler *delayScheduler = RACScheduler.currentScheduler ?: scheduler;
+ RACDisposable *schedulerDisposable = [delayScheduler afterDelay:interval schedule:block];
+ [disposable addDisposable:schedulerDisposable];
+ };
+
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ schedule(^{
+ [subscriber sendNext:x];
+ });
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ schedule(^{
+ [subscriber sendCompleted];
+ });
+ }];
+
+ [disposable addDisposable:subscriptionDisposable];
+ return disposable;
+ }] setNameWithFormat:@"[%@] -delay: %f", self.name, (double)interval];
+}
+
+- (RACSignal *)repeat {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return subscribeForever(self,
+ ^(id x) {
+ [subscriber sendNext:x];
+ },
+ ^(NSError *error, RACDisposable *disposable) {
+ [disposable dispose];
+ [subscriber sendError:error];
+ },
+ ^(RACDisposable *disposable) {
+ // Resubscribe.
+ });
+ }] setNameWithFormat:@"[%@] -repeat", self.name];
+}
+
+- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock {
+ NSCParameterAssert(catchBlock != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACSerialDisposable *catchDisposable = [[RACSerialDisposable alloc] init];
+
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ RACSignal *signal = catchBlock(error);
+ NSCAssert(signal != nil, @"Expected non-nil signal from catch block on %@", self);
+ catchDisposable.disposable = [signal subscribe:subscriber];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [catchDisposable dispose];
+ [subscriptionDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -catch:", self.name];
+}
+
+- (RACSignal *)catchTo:(RACSignal *)signal {
+ return [[self catch:^(NSError *error) {
+ return signal;
+ }] setNameWithFormat:@"[%@] -catchTo: %@", self.name, signal];
+}
+
+- (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock {
+ NSCParameterAssert(tryBlock != NULL);
+
+ return [[self flattenMap:^(id value) {
+ NSError *error = nil;
+ BOOL passed = tryBlock(value, &error);
+ return (passed ? [RACSignal return:value] : [RACSignal error:error]);
+ }] setNameWithFormat:@"[%@] -try:", self.name];
+}
+
+- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock {
+ NSCParameterAssert(mapBlock != NULL);
+
+ return [[self flattenMap:^(id value) {
+ NSError *error = nil;
+ id mappedValue = mapBlock(value, &error);
+ return (mappedValue == nil ? [RACSignal error:error] : [RACSignal return:mappedValue]);
+ }] setNameWithFormat:@"[%@] -tryMap:", self.name];
+}
+
+- (RACSignal *)initially:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[RACSignal defer:^{
+ block();
+ return self;
+ }] setNameWithFormat:@"[%@] -initially:", self.name];
+}
+
+- (RACSignal *)finally:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[[self
+ doError:^(NSError *error) {
+ block();
+ }]
+ doCompleted:^{
+ block();
+ }]
+ setNameWithFormat:@"[%@] -finally:", self.name];
+}
+
+- (RACSignal *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler {
+ NSCParameterAssert(scheduler != nil);
+ NSCParameterAssert(scheduler != RACScheduler.immediateScheduler);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACSerialDisposable *timerDisposable = [[RACSerialDisposable alloc] init];
+ NSMutableArray *values = [NSMutableArray array];
+
+ void (^flushValues)() = ^{
+ @synchronized (values) {
+ [timerDisposable.disposable dispose];
+
+ if (values.count == 0) return;
+
+ RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:values convertNullsToNils:NO];
+ [values removeAllObjects];
+ [subscriber sendNext:tuple];
+ }
+ };
+
+ RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
+ @synchronized (values) {
+ if (values.count == 0) {
+ timerDisposable.disposable = [scheduler afterDelay:interval schedule:flushValues];
+ }
+
+ [values addObject:x ?: RACTupleNil.tupleNil];
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ flushValues();
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [selfDisposable dispose];
+ [timerDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -bufferWithTime: %f", self.name, (double)interval];
+}
+
+- (RACSignal *)collect {
+ return [[self aggregateWithStartFactory:^{
+ return [[NSMutableArray alloc] init];
+ } reduce:^(NSMutableArray *collectedValues, id x) {
+ [collectedValues addObject:(x ?: NSNull.null)];
+ return collectedValues;
+ }] setNameWithFormat:@"[%@] -collect", self.name];
+}
+
+- (RACSignal *)takeLast:(NSUInteger)count {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ NSMutableArray *valuesTaken = [NSMutableArray arrayWithCapacity:count];
+ return [self subscribeNext:^(id x) {
+ [valuesTaken addObject:x ? : [RACTupleNil tupleNil]];
+
+ while(valuesTaken.count > count) {
+ [valuesTaken removeObjectAtIndex:0];
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ for(id value in valuesTaken) {
+ [subscriber sendNext:[value isKindOfClass:[RACTupleNil class]] ? nil : value];
+ }
+
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -takeLast: %lu", self.name, (unsigned long)count];
+}
+
+- (RACSignal *)combineLatestWith:(RACSignal *)signal {
+ NSCParameterAssert(signal != nil);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ __block id lastSelfValue = nil;
+ __block BOOL selfCompleted = NO;
+
+ __block id lastOtherValue = nil;
+ __block BOOL otherCompleted = NO;
+
+ void (^sendNext)(void) = ^{
+ @synchronized (disposable) {
+ if (lastSelfValue == nil || lastOtherValue == nil) return;
+ [subscriber sendNext:[RACTuple tupleWithObjects:lastSelfValue, lastOtherValue, nil]];
+ }
+ };
+
+ RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
+ @synchronized (disposable) {
+ lastSelfValue = x ?: RACTupleNil.tupleNil;
+ sendNext();
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ @synchronized (disposable) {
+ selfCompleted = YES;
+ if (otherCompleted) [subscriber sendCompleted];
+ }
+ }];
+
+ [disposable addDisposable:selfDisposable];
+
+ RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
+ @synchronized (disposable) {
+ lastOtherValue = x ?: RACTupleNil.tupleNil;
+ sendNext();
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ @synchronized (disposable) {
+ otherCompleted = YES;
+ if (selfCompleted) [subscriber sendCompleted];
+ }
+ }];
+
+ [disposable addDisposable:otherDisposable];
+
+ return disposable;
+ }] setNameWithFormat:@"[%@] -combineLatestWith: %@", self.name, signal];
+}
+
++ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals {
+ return [[self join:signals block:^(RACSignal *left, RACSignal *right) {
+ return [left combineLatestWith:right];
+ }] setNameWithFormat:@"+combineLatest: %@", signals];
+}
+
++ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock {
+ NSCParameterAssert(reduceBlock != nil);
+
+ RACSignal *result = [self combineLatest:signals];
+
+ // Although we assert this condition above, older versions of this method
+ // supported this argument being nil. Avoid crashing Release builds of
+ // apps that depended on that.
+ if (reduceBlock != nil) result = [result reduceEach:reduceBlock];
+
+ return [result setNameWithFormat:@"+combineLatest: %@ reduce:", signals];
+}
+
+- (RACSignal *)merge:(RACSignal *)signal {
+ return [[RACSignal
+ merge:@[ self, signal ]]
+ setNameWithFormat:@"[%@] -merge: %@", self.name, signal];
+}
+
++ (RACSignal *)merge:(id<NSFastEnumeration>)signals {
+ NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
+ for (RACSignal *signal in signals) {
+ [copiedSignals addObject:signal];
+ }
+
+ return [[[RACSignal
+ createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ for (RACSignal *signal in copiedSignals) {
+ [subscriber sendNext:signal];
+ }
+
+ [subscriber sendCompleted];
+ return nil;
+ }]
+ flatten]
+ setNameWithFormat:@"+merge: %@", copiedSignals];
+}
+
+- (RACSignal *)flatten:(NSUInteger)maxConcurrent {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init];
+
+ // Contains disposables for the currently active subscriptions.
+ //
+ // This should only be used while synchronized on `subscriber`.
+ NSMutableArray *activeDisposables = [[NSMutableArray alloc] initWithCapacity:maxConcurrent];
+
+ // Whether the signal-of-signals has completed yet.
+ //
+ // This should only be used while synchronized on `subscriber`.
+ __block BOOL selfCompleted = NO;
+
+ // Subscribes to the given signal.
+ //
+ // This will be set to nil once all signals have completed (to break
+ // a retain cycle in the recursive block).
+ __block void (^subscribeToSignal)(RACSignal *);
+
+ // Sends completed to the subscriber if all signals are finished.
+ //
+ // This should only be used while synchronized on `subscriber`.
+ void (^completeIfAllowed)(void) = ^{
+ if (selfCompleted && activeDisposables.count == 0) {
+ [subscriber sendCompleted];
+ subscribeToSignal = nil;
+ }
+ };
+
+ // The signals waiting to be started.
+ //
+ // This array should only be used while synchronized on `subscriber`.
+ NSMutableArray *queuedSignals = [NSMutableArray array];
+
+ subscribeToSignal = ^(RACSignal *signal) {
+ RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
+
+ @synchronized (subscriber) {
+ [compoundDisposable addDisposable:serialDisposable];
+ [activeDisposables addObject:serialDisposable];
+ }
+
+ serialDisposable.disposable = [signal subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ RACSignal *nextSignal;
+
+ @synchronized (subscriber) {
+ [compoundDisposable removeDisposable:serialDisposable];
+ [activeDisposables removeObjectIdenticalTo:serialDisposable];
+
+ if (queuedSignals.count == 0) {
+ completeIfAllowed();
+ return;
+ }
+
+ nextSignal = queuedSignals[0];
+ [queuedSignals removeObjectAtIndex:0];
+ }
+
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Warc-retain-cycles"
+ // This retain cycle is broken in `completeIfAllowed`.
+ subscribeToSignal(nextSignal);
+ #pragma clang diagnostic pop
+ }];
+ };
+
+ [compoundDisposable addDisposable:[self subscribeNext:^(RACSignal *signal) {
+ if (signal == nil) return;
+
+ NSCAssert([signal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", signal);
+
+ @synchronized (subscriber) {
+ if (maxConcurrent > 0 && activeDisposables.count >= maxConcurrent) {
+ [queuedSignals addObject:signal];
+
+ // If we need to wait, skip subscribing to this
+ // signal.
+ return;
+ }
+ }
+
+ subscribeToSignal(signal);
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ @synchronized (subscriber) {
+ selfCompleted = YES;
+ completeIfAllowed();
+ }
+ }]];
+
+ return compoundDisposable;
+ }] setNameWithFormat:@"[%@] -flatten: %lu", self.name, (unsigned long)maxConcurrent];
+}
+
+- (RACSignal *)then:(RACSignal * (^)(void))block {
+ NSCParameterAssert(block != nil);
+
+ return [[[self
+ ignoreValues]
+ concat:[RACSignal defer:block]]
+ setNameWithFormat:@"[%@] -then:", self.name];
+}
+
+- (RACSignal *)concat {
+ return [[self flatten:1] setNameWithFormat:@"[%@] -concat", self.name];
+}
+
+- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock {
+ NSCParameterAssert(startFactory != NULL);
+ NSCParameterAssert(reduceBlock != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block id runningValue = startFactory();
+ return [self subscribeNext:^(id x) {
+ runningValue = reduceBlock(runningValue, x);
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendNext:runningValue];
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -aggregateWithStartFactory:reduce:", self.name];
+}
+
+- (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock {
+ RACSignal *signal = [self aggregateWithStartFactory:^{
+ return start;
+ } reduce:reduceBlock];
+
+ return [signal setNameWithFormat:@"[%@] -aggregateWithStart: %@ reduce:", self.name, [start rac_description]];
+}
+
+- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object {
+ return [self setKeyPath:keyPath onObject:object nilValue:nil];
+}
+
+- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(id)nilValue {
+ NSCParameterAssert(keyPath != nil);
+ NSCParameterAssert(object != nil);
+
+ keyPath = [keyPath copy];
+
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ // Purposely not retaining 'object', since we want to tear down the binding
+ // when it deallocates normally.
+ __block void * volatile objectPtr = (__bridge void *)object;
+
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ // Possibly spec, possibly compiler bug, but this __bridge cast does not
+ // result in a retain here, effectively an invisible __unsafe_unretained
+ // qualifier. Using objc_precise_lifetime gives the __strong reference
+ // desired. The explicit use of __strong is strictly defensive.
+ __strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
+ [object setValue:x ?: nilValue forKeyPath:keyPath];
+ } error:^(NSError *error) {
+ __strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
+
+ NSCAssert(NO, @"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error);
+
+ // Log the error if we're running with assertions disabled.
+ NSLog(@"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error);
+
+ [disposable dispose];
+ } completed:^{
+ [disposable dispose];
+ }];
+
+ [disposable addDisposable:subscriptionDisposable];
+
+ #if DEBUG
+ static void *bindingsKey = &bindingsKey;
+ NSMutableDictionary *bindings;
+
+ @synchronized (object) {
+ bindings = objc_getAssociatedObject(object, bindingsKey);
+ if (bindings == nil) {
+ bindings = [NSMutableDictionary dictionary];
+ objc_setAssociatedObject(object, bindingsKey, bindings, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }
+ }
+
+ @synchronized (bindings) {
+ NSCAssert(bindings[keyPath] == nil, @"Signal %@ is already bound to key path \"%@\" on object %@, adding signal %@ is undefined behavior", [bindings[keyPath] nonretainedObjectValue], keyPath, object, self);
+
+ bindings[keyPath] = [NSValue valueWithNonretainedObject:self];
+ }
+ #endif
+
+ RACDisposable *clearPointerDisposable = [RACDisposable disposableWithBlock:^{
+ #if DEBUG
+ @synchronized (bindings) {
+ [bindings removeObjectForKey:keyPath];
+ }
+ #endif
+
+ while (YES) {
+ void *ptr = objectPtr;
+ if (OSAtomicCompareAndSwapPtrBarrier(ptr, NULL, &objectPtr)) {
+ break;
+ }
+ }
+ }];
+
+ [disposable addDisposable:clearPointerDisposable];
+
+ [object.rac_deallocDisposable addDisposable:disposable];
+
+ RACCompoundDisposable *objectDisposable = object.rac_deallocDisposable;
+ return [RACDisposable disposableWithBlock:^{
+ [objectDisposable removeDisposable:disposable];
+ [disposable dispose];
+ }];
+}
+
++ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler {
+ return [[RACSignal interval:interval onScheduler:scheduler withLeeway:0.0] setNameWithFormat:@"+interval: %f onScheduler: %@", (double)interval, scheduler];
+}
+
++ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway {
+ NSCParameterAssert(scheduler != nil);
+ NSCParameterAssert(scheduler != RACScheduler.immediateScheduler);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [scheduler after:[NSDate dateWithTimeIntervalSinceNow:interval] repeatingEvery:interval withLeeway:leeway schedule:^{
+ [subscriber sendNext:[NSDate date]];
+ }];
+ }] setNameWithFormat:@"+interval: %f onScheduler: %@ withLeeway: %f", (double)interval, scheduler, (double)leeway];
+}
+
+- (RACSignal *)takeUntil:(RACSignal *)signalTrigger {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+ void (^triggerCompletion)(void) = ^{
+ [disposable dispose];
+ [subscriber sendCompleted];
+ };
+
+ RACDisposable *triggerDisposable = [signalTrigger subscribeNext:^(id _) {
+ triggerCompletion();
+ } completed:^{
+ triggerCompletion();
+ }];
+
+ [disposable addDisposable:triggerDisposable];
+
+ if (!disposable.disposed) {
+ RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [disposable dispose];
+ [subscriber sendCompleted];
+ }];
+
+ [disposable addDisposable:selfDisposable];
+ }
+
+ return disposable;
+ }] setNameWithFormat:@"[%@] -takeUntil: %@", self.name, signalTrigger];
+}
+
+- (RACSignal *)takeUntilReplacement:(RACSignal *)replacement {
+ return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
+
+ RACDisposable *replacementDisposable = [replacement subscribeNext:^(id x) {
+ [selfDisposable dispose];
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [selfDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ [selfDisposable dispose];
+ [subscriber sendCompleted];
+ }];
+
+ if (!selfDisposable.disposed) {
+ selfDisposable.disposable = [[self
+ concat:[RACSignal never]]
+ subscribe:subscriber];
+ }
+
+ return [RACDisposable disposableWithBlock:^{
+ [selfDisposable dispose];
+ [replacementDisposable dispose];
+ }];
+ }];
+}
+
+- (RACSignal *)switchToLatest {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACMulticastConnection *connection = [self publish];
+
+ RACDisposable *subscriptionDisposable = [[connection.signal
+ flattenMap:^(RACSignal *x) {
+ NSCAssert(x == nil || [x isKindOfClass:RACSignal.class], @"-switchToLatest requires that the source signal (%@) send signals. Instead we got: %@", self, x);
+
+ // -concat:[RACSignal never] prevents completion of the receiver from
+ // prematurely terminating the inner signal.
+ return [x takeUntil:[connection.signal concat:[RACSignal never]]];
+ }]
+ subscribe:subscriber];
+
+ RACDisposable *connectionDisposable = [connection connect];
+ return [RACDisposable disposableWithBlock:^{
+ [subscriptionDisposable dispose];
+ [connectionDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -switchToLatest", self.name];
+}
+
++ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(RACSignal *)defaultSignal {
+ NSCParameterAssert(signal != nil);
+ NSCParameterAssert(cases != nil);
+
+ for (id key in cases) {
+ id value __attribute__((unused)) = cases[key];
+ NSCAssert([value isKindOfClass:RACSignal.class], @"Expected all cases to be RACSignals, %@ isn't", value);
+ }
+
+ NSDictionary *copy = [cases copy];
+
+ return [[[signal
+ map:^(id key) {
+ if (key == nil) key = RACTupleNil.tupleNil;
+
+ RACSignal *signal = copy[key] ?: defaultSignal;
+ if (signal == nil) {
+ NSString *description = [NSString stringWithFormat:NSLocalizedString(@"No matching signal found for value %@", @""), key];
+ return [RACSignal error:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorNoMatchingCase userInfo:@{ NSLocalizedDescriptionKey: description }]];
+ }
+
+ return signal;
+ }]
+ switchToLatest]
+ setNameWithFormat:@"+switch: %@ cases: %@ default: %@", signal, cases, defaultSignal];
+}
+
++ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal {
+ NSCParameterAssert(boolSignal != nil);
+ NSCParameterAssert(trueSignal != nil);
+ NSCParameterAssert(falseSignal != nil);
+
+ return [[[boolSignal
+ map:^(NSNumber *value) {
+ NSCAssert([value isKindOfClass:NSNumber.class], @"Expected %@ to send BOOLs, not %@", boolSignal, value);
+
+ return (value.boolValue ? trueSignal : falseSignal);
+ }]
+ switchToLatest]
+ setNameWithFormat:@"+if: %@ then: %@ else: %@", boolSignal, trueSignal, falseSignal];
+}
+
+- (id)first {
+ return [self firstOrDefault:nil];
+}
+
+- (id)firstOrDefault:(id)defaultValue {
+ return [self firstOrDefault:defaultValue success:NULL error:NULL];
+}
+
+- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error {
+ NSCondition *condition = [[NSCondition alloc] init];
+ condition.name = [NSString stringWithFormat:@"[%@] -firstOrDefault: %@ success:error:", self.name, defaultValue];
+
+ __block id value = defaultValue;
+ __block BOOL done = NO;
+
+ // Ensures that we don't pass values across thread boundaries by reference.
+ __block NSError *localError;
+ __block BOOL localSuccess;
+
+ [[self take:1] subscribeNext:^(id x) {
+ [condition lock];
+
+ value = x;
+ localSuccess = YES;
+
+ done = YES;
+ [condition broadcast];
+ [condition unlock];
+ } error:^(NSError *e) {
+ [condition lock];
+
+ if (!done) {
+ localSuccess = NO;
+ localError = e;
+
+ done = YES;
+ [condition broadcast];
+ }
+
+ [condition unlock];
+ } completed:^{
+ [condition lock];
+
+ localSuccess = YES;
+
+ done = YES;
+ [condition broadcast];
+ [condition unlock];
+ }];
+
+ [condition lock];
+ while (!done) {
+ [condition wait];
+ }
+
+ if (success != NULL) *success = localSuccess;
+ if (error != NULL) *error = localError;
+
+ [condition unlock];
+ return value;
+}
+
+- (BOOL)waitUntilCompleted:(NSError **)error {
+ BOOL success = NO;
+
+ [[[self
+ ignoreValues]
+ setNameWithFormat:@"[%@] -waitUntilCompleted:", self.name]
+ firstOrDefault:nil success:&success error:error];
+
+ return success;
+}
+
++ (RACSignal *)defer:(RACSignal * (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [block() subscribe:subscriber];
+ }] setNameWithFormat:@"+defer:"];
+}
+
+- (NSArray *)toArray {
+ return [[[self collect] first] copy];
+}
+
+- (RACSequence *)sequence {
+ return [[RACSignalSequence sequenceWithSignal:self] setNameWithFormat:@"[%@] -sequence", self.name];
+}
+
+- (RACMulticastConnection *)publish {
+ RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
+ RACMulticastConnection *connection = [self multicast:subject];
+ return connection;
+}
+
+- (RACMulticastConnection *)multicast:(RACSubject *)subject {
+ [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
+ RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
+ return connection;
+}
+
+- (RACSignal *)replay {
+ RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];
+
+ RACMulticastConnection *connection = [self multicast:subject];
+ [connection connect];
+
+ return connection.signal;
+}
+
+- (RACSignal *)replayLast {
+ RACReplaySubject *subject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"[%@] -replayLast", self.name];
+
+ RACMulticastConnection *connection = [self multicast:subject];
+ [connection connect];
+
+ return connection.signal;
+}
+
+- (RACSignal *)replayLazily {
+ RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]];
+ return [[RACSignal
+ defer:^{
+ [connection connect];
+ return connection.signal;
+ }]
+ setNameWithFormat:@"[%@] -replayLazily", self.name];
+}
+
+- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler {
+ NSCParameterAssert(scheduler != nil);
+ NSCParameterAssert(scheduler != RACScheduler.immediateScheduler);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ RACDisposable *timeoutDisposable = [scheduler afterDelay:interval schedule:^{
+ [disposable dispose];
+ [subscriber sendError:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorTimedOut userInfo:nil]];
+ }];
+
+ [disposable addDisposable:timeoutDisposable];
+
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [disposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ [disposable dispose];
+ [subscriber sendCompleted];
+ }];
+
+ [disposable addDisposable:subscriptionDisposable];
+ return disposable;
+ }] setNameWithFormat:@"[%@] -timeout: %f", self.name, (double)interval];
+}
+
+- (RACSignal *)deliverOn:(RACScheduler *)scheduler {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [self subscribeNext:^(id x) {
+ [scheduler schedule:^{
+ [subscriber sendNext:x];
+ }];
+ } error:^(NSError *error) {
+ [scheduler schedule:^{
+ [subscriber sendError:error];
+ }];
+ } completed:^{
+ [scheduler schedule:^{
+ [subscriber sendCompleted];
+ }];
+ }];
+ }] setNameWithFormat:@"[%@] -deliverOn: %@", self.name, scheduler];
+}
+
+- (RACSignal *)subscribeOn:(RACScheduler *)scheduler {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+
+ RACDisposable *schedulingDisposable = [scheduler schedule:^{
+ RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+
+ [disposable addDisposable:subscriptionDisposable];
+ }];
+
+ [disposable addDisposable:schedulingDisposable];
+ return disposable;
+ }] setNameWithFormat:@"[%@] -subscribeOn: %@", self.name, scheduler];
+}
+
+- (RACSignal *)groupBy:(id<NSCopying> (^)(id object))keyBlock transform:(id (^)(id object))transformBlock {
+ NSCParameterAssert(keyBlock != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ NSMutableDictionary *groups = [NSMutableDictionary dictionary];
+
+ return [self subscribeNext:^(id x) {
+ id<NSCopying> key = keyBlock(x);
+ RACGroupedSignal *groupSubject = nil;
+ @synchronized(groups) {
+ groupSubject = [groups objectForKey:key];
+ if(groupSubject == nil) {
+ groupSubject = [RACGroupedSignal signalWithKey:key];
+ [groups setObject:groupSubject forKey:key];
+ [subscriber sendNext:groupSubject];
+ }
+ }
+
+ [groupSubject sendNext:transformBlock != NULL ? transformBlock(x) : x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+
+ [groups.allValues makeObjectsPerformSelector:@selector(sendError:) withObject:error];
+ } completed:^{
+ [subscriber sendCompleted];
+
+ [groups.allValues makeObjectsPerformSelector:@selector(sendCompleted)];
+ }];
+ }] setNameWithFormat:@"[%@] -groupBy:transform:", self.name];
+}
+
+- (RACSignal *)groupBy:(id<NSCopying> (^)(id object))keyBlock {
+ return [[self groupBy:keyBlock transform:nil] setNameWithFormat:@"[%@] -groupBy:", self.name];
+}
+
+- (RACSignal *)any {
+ return [[self any:^(id x) {
+ return YES;
+ }] setNameWithFormat:@"[%@] -any", self.name];
+}
+
+- (RACSignal *)any:(BOOL (^)(id object))predicateBlock {
+ NSCParameterAssert(predicateBlock != NULL);
+
+ return [[[self materialize] bind:^{
+ return ^(RACEvent *event, BOOL *stop) {
+ if (event.finished) {
+ *stop = YES;
+ return [RACSignal return:@NO];
+ }
+
+ if (predicateBlock(event.value)) {
+ *stop = YES;
+ return [RACSignal return:@YES];
+ }
+
+ return [RACSignal empty];
+ };
+ }] setNameWithFormat:@"[%@] -any:", self.name];
+}
+
+- (RACSignal *)all:(BOOL (^)(id object))predicateBlock {
+ NSCParameterAssert(predicateBlock != NULL);
+
+ return [[[self materialize] bind:^{
+ return ^(RACEvent *event, BOOL *stop) {
+ if (event.eventType == RACEventTypeCompleted) {
+ *stop = YES;
+ return [RACSignal return:@YES];
+ }
+
+ if (event.eventType == RACEventTypeError || !predicateBlock(event.value)) {
+ *stop = YES;
+ return [RACSignal return:@NO];
+ }
+
+ return [RACSignal empty];
+ };
+ }] setNameWithFormat:@"[%@] -all:", self.name];
+}
+
+- (RACSignal *)retry:(NSInteger)retryCount {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block NSInteger currentRetryCount = 0;
+ return subscribeForever(self,
+ ^(id x) {
+ [subscriber sendNext:x];
+ },
+ ^(NSError *error, RACDisposable *disposable) {
+ if (retryCount == 0 || currentRetryCount < retryCount) {
+ // Resubscribe.
+ currentRetryCount++;
+ return;
+ }
+
+ [disposable dispose];
+ [subscriber sendError:error];
+ },
+ ^(RACDisposable *disposable) {
+ [disposable dispose];
+ [subscriber sendCompleted];
+ });
+ }] setNameWithFormat:@"[%@] -retry: %lu", self.name, (unsigned long)retryCount];
+}
+
+- (RACSignal *)retry {
+ return [[self retry:0] setNameWithFormat:@"[%@] -retry", self.name];
+}
+
+- (RACSignal *)sample:(RACSignal *)sampler {
+ NSCParameterAssert(sampler != nil);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ NSLock *lock = [[NSLock alloc] init];
+ __block id lastValue;
+ __block BOOL hasValue = NO;
+
+ RACSerialDisposable *samplerDisposable = [[RACSerialDisposable alloc] init];
+ RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
+ [lock lock];
+ hasValue = YES;
+ lastValue = x;
+ [lock unlock];
+ } error:^(NSError *error) {
+ [samplerDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ [samplerDisposable dispose];
+ [subscriber sendCompleted];
+ }];
+
+ samplerDisposable.disposable = [sampler subscribeNext:^(id _) {
+ BOOL shouldSend = NO;
+ id value;
+ [lock lock];
+ shouldSend = hasValue;
+ value = lastValue;
+ [lock unlock];
+
+ if (shouldSend) {
+ [subscriber sendNext:value];
+ }
+ } error:^(NSError *error) {
+ [sourceDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ [sourceDisposable dispose];
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [samplerDisposable dispose];
+ [sourceDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -sample: %@", self.name, sampler];
+}
+
+- (RACSignal *)ignoreValues {
+ return [[self filter:^(id _) {
+ return NO;
+ }] setNameWithFormat:@"[%@] -ignoreValues", self.name];
+}
+
+- (RACSignal *)materialize {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [self subscribeNext:^(id x) {
+ [subscriber sendNext:[RACEvent eventWithValue:x]];
+ } error:^(NSError *error) {
+ [subscriber sendNext:[RACEvent eventWithError:error]];
+ [subscriber sendCompleted];
+ } completed:^{
+ [subscriber sendNext:RACEvent.completedEvent];
+ [subscriber sendCompleted];
+ }];
+ }] setNameWithFormat:@"[%@] -materialize", self.name];
+}
+
+- (RACSignal *)dematerialize {
+ return [[self bind:^{
+ return ^(RACEvent *event, BOOL *stop) {
+ switch (event.eventType) {
+ case RACEventTypeCompleted:
+ *stop = YES;
+ return [RACSignal empty];
+
+ case RACEventTypeError:
+ *stop = YES;
+ return [RACSignal error:event.error];
+
+ case RACEventTypeNext:
+ return [RACSignal return:event.value];
+ }
+ };
+ }] setNameWithFormat:@"[%@] -dematerialize", self.name];
+}
+
+- (RACSignal *)not {
+ return [[self map:^(NSNumber *value) {
+ NSCAssert([value isKindOfClass:NSNumber.class], @"-not must only be used on a signal of NSNumbers. Instead, got: %@", value);
+
+ return @(!value.boolValue);
+ }] setNameWithFormat:@"[%@] -not", self.name];
+}
+
+- (RACSignal *)and {
+ return [[self map:^(RACTuple *tuple) {
+ NSCAssert([tuple isKindOfClass:RACTuple.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
+ NSCAssert(tuple.count > 0, @"-and must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
+
+ return @([tuple.rac_sequence all:^(NSNumber *number) {
+ NSCAssert([number isKindOfClass:NSNumber.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
+
+ return number.boolValue;
+ }]);
+ }] setNameWithFormat:@"[%@] -and", self.name];
+}
+
+- (RACSignal *)or {
+ return [[self map:^(RACTuple *tuple) {
+ NSCAssert([tuple isKindOfClass:RACTuple.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
+ NSCAssert(tuple.count > 0, @"-or must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
+
+ return @([tuple.rac_sequence any:^(NSNumber *number) {
+ NSCAssert([number isKindOfClass:NSNumber.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
+
+ return number.boolValue;
+ }]);
+ }] setNameWithFormat:@"[%@] -or", self.name];
+}
+
+@end
+
+@implementation RACSignal (OperationsDeprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (RACSignal *)windowWithStart:(RACSignal *)openSignal close:(RACSignal * (^)(RACSignal *start))closeBlock {
+ NSCParameterAssert(openSignal != nil);
+ NSCParameterAssert(closeBlock != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block RACSubject *currentWindow = nil;
+ __block RACSignal *currentCloseWindow = nil;
+ __block RACDisposable *closeObserverDisposable = NULL;
+
+ void (^closeCurrentWindow)(void) = ^{
+ [currentWindow sendCompleted];
+ currentWindow = nil;
+ currentCloseWindow = nil;
+ [closeObserverDisposable dispose], closeObserverDisposable = nil;
+ };
+
+ RACDisposable *openObserverDisposable = [openSignal subscribe:[RACSubscriber subscriberWithNext:^(id x) {
+ if(currentWindow == nil) {
+ currentWindow = [RACSubject subject];
+ [subscriber sendNext:currentWindow];
+
+ currentCloseWindow = closeBlock(currentWindow);
+ closeObserverDisposable = [currentCloseWindow subscribe:[RACSubscriber subscriberWithNext:^(id x) {
+ closeCurrentWindow();
+ } error:^(NSError *error) {
+ closeCurrentWindow();
+ } completed:^{
+ closeCurrentWindow();
+ }]];
+ }
+ } error:^(NSError *error) {
+
+ } completed:^{
+
+ }]];
+
+ RACDisposable *selfObserverDisposable = [self subscribeNext:^(id x) {
+ [currentWindow sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [closeObserverDisposable dispose];
+ [openObserverDisposable dispose];
+ [selfObserverDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -windowWithStart: %@ close:", self.name, openSignal];
+}
+
+- (RACSignal *)buffer:(NSUInteger)bufferCount {
+ NSCParameterAssert(bufferCount > 0);
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ NSMutableArray *values = [NSMutableArray arrayWithCapacity:bufferCount];
+ RACSubject *windowCloseSubject = [RACSubject subject];
+
+ RACDisposable *closeDisposable = [windowCloseSubject subscribeNext:^(id x) {
+ [subscriber sendNext:[RACTuple tupleWithObjectsFromArray:values convertNullsToNils:NO]];
+ [values removeAllObjects];
+ }];
+
+ __block RACDisposable *innerDisposable = nil;
+ RACDisposable *outerDisposable = [[self windowWithStart:self close:^(RACSignal *start) {
+ return windowCloseSubject;
+ }] subscribeNext:^(id x) {
+ innerDisposable = [x subscribeNext:^(id x) {
+ [values addObject:x ? : [RACTupleNil tupleNil]];
+ if(values.count % bufferCount == 0) {
+ [windowCloseSubject sendNext:[RACUnit defaultUnit]];
+ }
+ }];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [innerDisposable dispose];
+ [outerDisposable dispose];
+ [closeDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -buffer: %lu", self.name, (unsigned long)bufferCount];
+}
+
+- (RACSignal *)let:(RACSignal * (^)(RACSignal *sharedSignal))letBlock {
+ NSCParameterAssert(letBlock != NULL);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACMulticastConnection *connection = [self publish];
+ RACDisposable *finalDisposable = [letBlock(connection.signal) subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ [subscriber sendCompleted];
+ }];
+
+ RACDisposable *connectionDisposable = [connection connect];
+
+ return [RACDisposable disposableWithBlock:^{
+ [connectionDisposable dispose];
+ [finalDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -let:", self.name];
+}
+
++ (RACSignal *)interval:(NSTimeInterval)interval {
+ return [RACSignal interval:interval onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh]];
+}
+
++ (RACSignal *)interval:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway {
+ return [RACSignal interval:interval onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh] withLeeway:leeway];
+}
+
+- (RACSignal *)timeout:(NSTimeInterval)interval {
+ return [self timeout:interval onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh]];
+}
+
+- (RACSignal *)bufferWithTime:(NSTimeInterval)interval {
+ return [self bufferWithTime:interval onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh]];
+}
+
+- (RACDisposable *)toProperty:(NSString *)keyPath onObject:(NSObject *)object {
+ return [self setKeyPath:keyPath onObject:object];
+}
+
+- (RACSignal *)ignoreElements {
+ return [self ignoreValues];
+}
+
+- (RACSignal *)sequenceNext:(RACSignal * (^)(void))block {
+ return [self then:block];
+}
+
+- (RACSignal *)aggregateWithStart:(id)start combine:(id (^)(id running, id next))combineBlock {
+ return [self aggregateWithStart:start reduce:combineBlock];
+}
+
+- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory combine:(id (^)(id running, id next))combineBlock {
+ return [self aggregateWithStartFactory:startFactory reduce:combineBlock];
+}
+
+- (RACDisposable *)executeCommand:(RACCommand *)command {
+ NSCParameterAssert(command != nil);
+
+ return [self subscribeNext:^(id x) {
+ [command execute:x];
+ }];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
new file mode 100644
index 0000000..ed644cf
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
@@ -0,0 +1,219 @@
+//
+// RACSignal.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/1/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "RACStream.h"
+
+@class RACDisposable;
+@class RACScheduler;
+@class RACSubject;
+@protocol RACSubscriber;
+
+@interface RACSignal : RACStream
+
+/// Creates a new signal. This is the preferred way to create a new signal
+/// operation or behavior.
+///
+/// Events can be sent to new subscribers immediately in the `didSubscribe`
+/// block, but the subscriber will not be able to dispose of the signal until
+/// a RACDisposable is returned from `didSubscribe`. In the case of infinite
+/// signals, this won't _ever_ happen if events are sent immediately.
+///
+/// To ensure that the signal is disposable, events can be scheduled on the
+/// +[RACScheduler currentScheduler] (so that they're deferred, not sent
+/// immediately), or they can be sent in the background. The RACDisposable
+/// returned by the `didSubscribe` block should cancel any such scheduling or
+/// asynchronous work.
+///
+/// didSubscribe - Called when the signal is subscribed to. The new subscriber is
+/// passed in. You can then manually control the <RACSubscriber> by
+/// sending it -sendNext:, -sendError:, and -sendCompleted,
+/// as defined by the operation you're implementing. This block
+/// should return a RACDisposable which cancels any ongoing work
+/// triggered by the subscription, and cleans up any resources or
+/// disposables created as part of it. When the disposable is
+/// disposed of, the signal must not send any more events to the
+/// `subscriber`. If no cleanup is necessary, return nil.
+///
+/// **Note:** The `didSubscribe` block is called every time a new subscriber
+/// subscribes. Any side effects within the block will thus execute once for each
+/// subscription, not necessarily on one thread, and possibly even
+/// simultaneously!
++ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
+
+/// Returns a signal that immediately sends the given error.
++ (RACSignal *)error:(NSError *)error;
+
+/// Returns a signal that never completes.
++ (RACSignal *)never;
+
+/// Immediately schedules the given block on the given scheduler. The block is
+/// given a subscriber to which it can send events.
+///
+/// scheduler - The scheduler on which `block` will be scheduled and results
+/// delivered. Cannot be nil.
+/// block - The block to invoke. Cannot be NULL.
+///
+/// Returns a signal which will send all events sent on the subscriber given to
+/// `block`. All events will be sent on `scheduler` and it will replay any missed
+/// events to new subscribers.
++ (RACSignal *)startEagerlyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id<RACSubscriber> subscriber))block;
+
+/// Invokes the given block only on the first subscription. The block is given a
+/// subscriber to which it can send events.
+///
+/// Note that disposing of the subscription to the returned signal will *not*
+/// dispose of the underlying subscription. If you need that behavior, see
+/// -[RACMulticastConnection autoconnect]. The underlying subscription will never
+/// be disposed of. Because of this, `block` should never return an infinite
+/// signal since there would be no way of ending it.
+///
+/// scheduler - The scheduler on which the block should be scheduled. Note that
+/// if given +[RACScheduler immediateScheduler], the block will be
+/// invoked synchronously on the first subscription. Cannot be nil.
+/// block - The block to invoke on the first subscription. Cannot be NULL.
+///
+/// Returns a signal which will pass through the events sent to the subscriber
+/// given to `block` and replay any missed events to new subscribers.
++ (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id<RACSubscriber> subscriber))block;
+
+@end
+
+@interface RACSignal (RACStream)
+
+/// Returns a signal that immediately sends the given value and then completes.
++ (RACSignal *)return:(id)value;
+
+/// Returns a signal that immediately completes.
++ (RACSignal *)empty;
+
+/// Subscribes to `signal` when the source signal completes.
+- (RACSignal *)concat:(RACSignal *)signal;
+
+/// Zips the values in the receiver with those of the given signal to create
+/// RACTuples.
+///
+/// The first `next` of each stream will be combined, then the second `next`, and
+/// so forth, until either signal completes or errors.
+///
+/// signal - The signal to zip with. This must not be `nil`.
+///
+/// Returns a new signal of RACTuples, representing the combined values of the
+/// two signals. Any error from one of the original signals will be forwarded on
+/// the returned signal.
+- (RACSignal *)zipWith:(RACSignal *)signal;
+
+@end
+
+@interface RACSignal (Subscription)
+
+/// Subscribes `subscriber` to changes on the receiver. The receiver defines which
+/// events it actually sends and in what situations the events are sent.
+///
+/// Subscription will always happen on a valid RACScheduler. If the
+/// +[RACScheduler currentScheduler] cannot be determined at the time of
+/// subscription (e.g., because the calling code is running on a GCD queue or
+/// NSOperationQueue), subscription will occur on a private background scheduler.
+/// On the main thread, subscriptions will always occur immediately, with a
+/// +[RACScheduler currentScheduler] of +[RACScheduler mainThreadScheduler].
+///
+/// This method must be overridden by any subclasses.
+///
+/// Returns nil or a disposable. You can call -[RACDisposable dispose] if you
+/// need to end your subscription before it would "naturally" end, either by
+/// completing or erroring. Once the disposable has been disposed, the subscriber
+/// won't receive any more events from the subscription.
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber;
+
+/// Convenience method to subscribe to the `next` event.
+///
+/// This corresponds to `IObserver<T>.OnNext` in Rx.
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock;
+
+/// Convenience method to subscribe to the `next` and `completed` events.
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock;
+
+/// Convenience method to subscribe to the `next`, `completed`, and `error` events.
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;
+
+/// Convenience method to subscribe to `error` events.
+///
+/// This corresponds to the `IObserver<T>.OnError` in Rx.
+- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock;
+
+/// Convenience method to subscribe to `completed` events.
+///
+/// This corresponds to the `IObserver<T>.OnCompleted` in Rx.
+- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock;
+
+/// Convenience method to subscribe to `next` and `error` events.
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock;
+
+/// Convenience method to subscribe to `error` and `completed` events.
+- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;
+
+@end
+
+/// Additional methods to assist with debugging.
+@interface RACSignal (Debugging)
+
+/// Logs all events that the receiver sends.
+- (RACSignal *)logAll;
+
+/// Logs each `next` that the receiver sends.
+- (RACSignal *)logNext;
+
+/// Logs any error that the receiver sends.
+- (RACSignal *)logError;
+
+/// Logs any `completed` event that the receiver sends.
+- (RACSignal *)logCompleted;
+
+@end
+
+/// Additional methods to assist with unit testing.
+///
+/// **These methods should never ship in production code.**
+@interface RACSignal (Testing)
+
+/// Spins the main run loop for a short while, waiting for the receiver to send a `next`.
+///
+/// **Because this method executes the run loop recursively, it should only be used
+/// on the main thread, and only from a unit test.**
+///
+/// defaultValue - Returned if the receiver completes or errors before sending
+/// a `next`, or if the method times out. This argument may be
+/// nil.
+/// success - If not NULL, set to whether the receiver completed
+/// successfully.
+/// error - If not NULL, set to any error that occurred.
+///
+/// Returns the first value received, or `defaultValue` if no value is received
+/// before the signal finishes or the method times out.
+- (id)asynchronousFirstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error;
+
+/// Spins the main run loop for a short while, waiting for the receiver to complete.
+///
+/// **Because this method executes the run loop recursively, it should only be used
+/// on the main thread, and only from a unit test.**
+///
+/// error - If not NULL, set to any error that occurs.
+///
+/// Returns whether the signal completed successfully before timing out. If NO,
+/// `error` will be set to any error that occurred.
+- (BOOL)asynchronouslyWaitUntilCompleted:(NSError **)error;
+
+@end
+
+@interface RACSignal (Deprecated)
+
++ (RACSignal *)start:(id (^)(BOOL *success, NSError **error))block __attribute__((deprecated("Use +startEagerlyWithScheduler:block: instead")));
++ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler subjectBlock:(void (^)(RACSubject *subject))block __attribute__((deprecated("Use +startEagerlyWithScheduler:block: instead")));
++ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler block:(id (^)(BOOL *success, NSError **error))block __attribute__((deprecated("Use +startEagerlyWithScheduler:block: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.m
new file mode 100644
index 0000000..69d9700
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.m
@@ -0,0 +1,447 @@
+//
+// RACSignal.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/15/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACDynamicSignal.h"
+#import "RACEmptySignal.h"
+#import "RACErrorSignal.h"
+#import "RACMulticastConnection.h"
+#import "RACReplaySubject.h"
+#import "RACReturnSignal.h"
+#import "RACScheduler.h"
+#import "RACSerialDisposable.h"
+#import "RACSignal+Operations.h"
+#import "RACSubject.h"
+#import "RACSubscriber+Private.h"
+#import "RACTuple.h"
+
+@implementation RACSignal
+
+#pragma mark Lifecycle
+
++ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
+ return [RACDynamicSignal createSignal:didSubscribe];
+}
+
++ (RACSignal *)error:(NSError *)error {
+ return [RACErrorSignal error:error];
+}
+
++ (RACSignal *)never {
+ return [[self createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ return nil;
+ }] setNameWithFormat:@"+never"];
+}
+
++ (RACSignal *)startEagerlyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id<RACSubscriber> subscriber))block {
+ NSCParameterAssert(scheduler != nil);
+ NSCParameterAssert(block != NULL);
+
+ RACSignal *signal = [self startLazilyWithScheduler:scheduler block:block];
+ // Subscribe to force the lazy signal to call its block.
+ [[signal publish] connect];
+ return [signal setNameWithFormat:@"+startEagerlyWithScheduler:%@ block:", scheduler];
+}
+
++ (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id<RACSubscriber> subscriber))block {
+ NSCParameterAssert(scheduler != nil);
+ NSCParameterAssert(block != NULL);
+
+ RACMulticastConnection *connection = [[RACSignal
+ createSignal:^ id (id<RACSubscriber> subscriber) {
+ block(subscriber);
+ return nil;
+ }]
+ multicast:[RACReplaySubject subject]];
+
+ return [[[RACSignal
+ createSignal:^ id (id<RACSubscriber> subscriber) {
+ [connection.signal subscribe:subscriber];
+ [connection connect];
+ return nil;
+ }]
+ subscribeOn:scheduler]
+ setNameWithFormat:@"+startLazilyWithScheduler:%@ block:", scheduler];
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p> name: %@", self.class, self, self.name];
+}
+
+@end
+
+@implementation RACSignal (RACStream)
+
++ (RACSignal *)empty {
+ return [RACEmptySignal empty];
+}
+
++ (RACSignal *)return:(id)value {
+ return [RACReturnSignal return:value];
+}
+
+- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ /*
+ * -bind: should:
+ *
+ * 1. Subscribe to the original signal of values.
+ * 2. Any time the original signal sends a value, transform it using the binding block.
+ * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
+ * 4. If the binding block asks the bind to terminate, complete the _original_ signal.
+ * 5. When _all_ signals complete, send completed to the subscriber.
+ *
+ * If any signal sends an error at any point, send that to the subscriber.
+ */
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACStreamBindBlock bindingBlock = block();
+
+ NSMutableArray *signals = [NSMutableArray arrayWithObject:self];
+
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
+ BOOL removeDisposable = NO;
+
+ @synchronized (signals) {
+ [signals removeObject:signal];
+
+ if (signals.count == 0) {
+ [subscriber sendCompleted];
+ [compoundDisposable dispose];
+ } else {
+ removeDisposable = YES;
+ }
+ }
+
+ if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];
+ };
+
+ void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
+ @synchronized (signals) {
+ [signals addObject:signal];
+ }
+
+ RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
+ [compoundDisposable addDisposable:selfDisposable];
+
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [compoundDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ @autoreleasepool {
+ completeSignal(signal, selfDisposable);
+ }
+ }];
+
+ selfDisposable.disposable = disposable;
+ };
+
+ @autoreleasepool {
+ RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
+ [compoundDisposable addDisposable:selfDisposable];
+
+ RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
+ BOOL stop = NO;
+ id signal = bindingBlock(x, &stop);
+
+ @autoreleasepool {
+ if (signal != nil) addSignal(signal);
+ if (signal == nil || stop) {
+ [selfDisposable dispose];
+ completeSignal(self, selfDisposable);
+ }
+ }
+ } error:^(NSError *error) {
+ [compoundDisposable dispose];
+ [subscriber sendError:error];
+ } completed:^{
+ @autoreleasepool {
+ completeSignal(self, selfDisposable);
+ }
+ }];
+
+ selfDisposable.disposable = bindingDisposable;
+ }
+
+ return compoundDisposable;
+ }] setNameWithFormat:@"[%@] -bind:", self.name];
+}
+
+- (RACSignal *)concat:(RACSignal *)signal {
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
+
+ RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
+ [subscriber sendNext:x];
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ RACDisposable *concattedDisposable = [signal subscribe:subscriber];
+ serialDisposable.disposable = concattedDisposable;
+ }];
+
+ serialDisposable.disposable = sourceDisposable;
+ return serialDisposable;
+ }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
+}
+
+- (RACSignal *)zipWith:(RACSignal *)signal {
+ NSCParameterAssert(signal != nil);
+
+ return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block BOOL selfCompleted = NO;
+ NSMutableArray *selfValues = [NSMutableArray array];
+
+ __block BOOL otherCompleted = NO;
+ NSMutableArray *otherValues = [NSMutableArray array];
+
+ void (^sendCompletedIfNecessary)(void) = ^{
+ @synchronized (selfValues) {
+ BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
+ BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
+ if (selfEmpty || otherEmpty) [subscriber sendCompleted];
+ }
+ };
+
+ void (^sendNext)(void) = ^{
+ @synchronized (selfValues) {
+ if (selfValues.count == 0) return;
+ if (otherValues.count == 0) return;
+
+ RACTuple *tuple = [RACTuple tupleWithObjects:selfValues[0], otherValues[0], nil];
+ [selfValues removeObjectAtIndex:0];
+ [otherValues removeObjectAtIndex:0];
+
+ [subscriber sendNext:tuple];
+ sendCompletedIfNecessary();
+ }
+ };
+
+ RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
+ @synchronized (selfValues) {
+ [selfValues addObject:x ?: RACTupleNil.tupleNil];
+ sendNext();
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ @synchronized (selfValues) {
+ selfCompleted = YES;
+ sendCompletedIfNecessary();
+ }
+ }];
+
+ RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
+ @synchronized (selfValues) {
+ [otherValues addObject:x ?: RACTupleNil.tupleNil];
+ sendNext();
+ }
+ } error:^(NSError *error) {
+ [subscriber sendError:error];
+ } completed:^{
+ @synchronized (selfValues) {
+ otherCompleted = YES;
+ sendCompletedIfNecessary();
+ }
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [selfDisposable dispose];
+ [otherDisposable dispose];
+ }];
+ }] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
+}
+
+@end
+
+@implementation RACSignal (Subscription)
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCAssert(NO, @"This method must be overridden by subclasses");
+ return nil;
+}
+
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
+ NSCParameterAssert(nextBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock {
+ NSCParameterAssert(nextBlock != NULL);
+ NSCParameterAssert(completedBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:completedBlock];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
+ NSCParameterAssert(nextBlock != NULL);
+ NSCParameterAssert(errorBlock != NULL);
+ NSCParameterAssert(completedBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock {
+ NSCParameterAssert(errorBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:errorBlock completed:NULL];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock {
+ NSCParameterAssert(completedBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:NULL completed:completedBlock];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock {
+ NSCParameterAssert(nextBlock != NULL);
+ NSCParameterAssert(errorBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:NULL];
+ return [self subscribe:o];
+}
+
+- (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(void (^)(void))completedBlock {
+ NSCParameterAssert(completedBlock != NULL);
+ NSCParameterAssert(errorBlock != NULL);
+
+ RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:errorBlock completed:completedBlock];
+ return [self subscribe:o];
+}
+
+@end
+
+@implementation RACSignal (Debugging)
+
+- (RACSignal *)logAll {
+ return [[[self logNext] logError] logCompleted];
+}
+
+- (RACSignal *)logNext {
+ return [[self doNext:^(id x) {
+ NSLog(@"%@ next: %@", self, x);
+ }] setNameWithFormat:@"%@", self.name];
+}
+
+- (RACSignal *)logError {
+ return [[self doError:^(NSError *error) {
+ NSLog(@"%@ error: %@", self, error);
+ }] setNameWithFormat:@"%@", self.name];
+}
+
+- (RACSignal *)logCompleted {
+ return [[self doCompleted:^{
+ NSLog(@"%@ completed", self);
+ }] setNameWithFormat:@"%@", self.name];
+}
+
+@end
+
+@implementation RACSignal (Testing)
+
+static const NSTimeInterval RACSignalAsynchronousWaitTimeout = 10;
+
+- (id)asynchronousFirstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error {
+ NSCAssert([NSThread isMainThread], @"%s should only be used from the main thread", __func__);
+
+ __block id result = defaultValue;
+ __block BOOL done = NO;
+
+ // Ensures that we don't pass values across thread boundaries by reference.
+ __block NSError *localError;
+ __block BOOL localSuccess = YES;
+
+ [[[[self
+ take:1]
+ timeout:RACSignalAsynchronousWaitTimeout onScheduler:[RACScheduler scheduler]]
+ deliverOn:RACScheduler.mainThreadScheduler]
+ subscribeNext:^(id x) {
+ result = x;
+ done = YES;
+ } error:^(NSError *e) {
+ if (!done) {
+ localSuccess = NO;
+ localError = e;
+ done = YES;
+ }
+ } completed:^{
+ done = YES;
+ }];
+
+ do {
+ [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+ } while (!done);
+
+ if (success != NULL) *success = localSuccess;
+ if (error != NULL) *error = localError;
+
+ return result;
+}
+
+- (BOOL)asynchronouslyWaitUntilCompleted:(NSError **)error {
+ BOOL success = NO;
+ [[self ignoreValues] asynchronousFirstOrDefault:nil success:&success error:error];
+ return success;
+}
+
+@end
+
+@implementation RACSignal (Deprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
++ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler subjectBlock:(void (^)(RACSubject *subject))block {
+ NSCParameterAssert(block != NULL);
+
+ RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"+startWithScheduler:subjectBlock:"];
+
+ [scheduler schedule:^{
+ block(subject);
+ }];
+
+ return subject;
+}
+
++ (RACSignal *)start:(id (^)(BOOL *success, NSError **error))block {
+ return [[self startWithScheduler:[RACScheduler scheduler] block:block] setNameWithFormat:@"+start:"];
+}
+
++ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler block:(id (^)(BOOL *success, NSError **error))block {
+ return [[self startWithScheduler:scheduler subjectBlock:^(id<RACSubscriber> subscriber) {
+ BOOL success = YES;
+ NSError *error = nil;
+ id returned = block(&success, &error);
+
+ if (!success) {
+ [subscriber sendError:error];
+ } else {
+ [subscriber sendNext:returned];
+ [subscriber sendCompleted];
+ }
+ }] setNameWithFormat:@"+startWithScheduler:block:"];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignalProvider.d b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalProvider.d
new file mode 100644
index 0000000..8add9a1
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalProvider.d
@@ -0,0 +1,5 @@
+provider RACSignal {
+ probe next(char *signal, char *subscriber, char *valueDescription);
+ probe completed(char *signal, char *subscriber);
+ probe error(char *signal, char *subscriber, char *errorDescription);
+};
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.h
new file mode 100644
index 0000000..e3ab77d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.h
@@ -0,0 +1,19 @@
+//
+// RACSignalSequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-09.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+@class RACSignal;
+
+// Private class that adapts a RACSignal to the RACSequence interface.
+@interface RACSignalSequence : RACSequence
+
+// Returns a sequence for enumerating over the given signal.
++ (RACSequence *)sequenceWithSignal:(RACSignal *)signal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.m
new file mode 100644
index 0000000..52ea1b9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.m
@@ -0,0 +1,79 @@
+//
+// RACSignalSequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-09.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignalSequence.h"
+#import "RACDisposable.h"
+#import "RACReplaySubject.h"
+#import "RACSignal+Operations.h"
+
+@interface RACSignalSequence ()
+
+// Replays the signal given on initialization.
+@property (nonatomic, strong, readonly) RACReplaySubject *subject;
+
+@end
+
+@implementation RACSignalSequence
+
+#pragma mark Lifecycle
+
++ (RACSequence *)sequenceWithSignal:(RACSignal *)signal {
+ RACSignalSequence *seq = [[self alloc] init];
+
+ RACReplaySubject *subject = [RACReplaySubject subject];
+ [signal subscribeNext:^(id value) {
+ [subject sendNext:value];
+ } error:^(NSError *error) {
+ [subject sendError:error];
+ } completed:^{
+ [subject sendCompleted];
+ }];
+
+ seq->_subject = subject;
+ return seq;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ id value = [self.subject firstOrDefault:self];
+
+ if (value == self) {
+ return nil;
+ } else {
+ return value ?: NSNull.null;
+ }
+}
+
+- (RACSequence *)tail {
+ RACSequence *sequence = [self.class sequenceWithSignal:[self.subject skip:1]];
+ sequence.name = self.name;
+ return sequence;
+}
+
+- (NSArray *)array {
+ return self.subject.toArray;
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ // Synchronously accumulate the values that have been sent so far.
+ NSMutableArray *values = [NSMutableArray array];
+ RACDisposable *disposable = [self.subject subscribeNext:^(id value) {
+ @synchronized (values) {
+ [values addObject:value ?: NSNull.null];
+ }
+ }];
+
+ [disposable dispose];
+
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, values = %@ … }", self.class, self, self.name, values];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACStream+Private.h b/ReactiveCocoaFramework/ReactiveCocoa/RACStream+Private.h
new file mode 100644
index 0000000..f6c0407
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACStream+Private.h
@@ -0,0 +1,23 @@
+//
+// RACStream+Private.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACStream.h"
+
+@interface RACStream ()
+
+// Combines a list of streams using the logic of the given block.
+//
+// streams - The streams to combine.
+// block - An operator that combines two streams and returns a new one. The
+// returned stream should contain 2-tuples of the streams' combined
+// values.
+//
+// Returns a combined stream.
++ (instancetype)join:(id<NSFastEnumeration>)streams block:(RACStream * (^)(id, id))block;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACStream.h b/ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
new file mode 100644
index 0000000..8853ffe
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACStream.h
@@ -0,0 +1,321 @@
+//
+// RACStream.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-31.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACStream;
+
+/// A block which accepts a value from a RACStream and returns a new instance
+/// of the same stream class.
+///
+/// Setting `stop` to `YES` will cause the bind to terminate after the returned
+/// value. Returning `nil` will result in immediate termination.
+typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);
+
+/// An abstract class representing any stream of values.
+///
+/// This class represents a monad, upon which many stream-based operations can
+/// be built.
+///
+/// When subclassing RACStream, only the methods in the main @interface body need
+/// to be overridden.
+@interface RACStream : NSObject
+
+/// Returns an empty stream.
++ (instancetype)empty;
+
+/// Lifts `value` into the stream monad.
+///
+/// Returns a stream containing only the given value.
++ (instancetype)return:(id)value;
+
+/// Lazily binds a block to the values in the receiver.
+///
+/// This should only be used if you need to terminate the bind early, or close
+/// over some state. -flattenMap: is more appropriate for all other cases.
+///
+/// block - A block returning a RACStreamBindBlock. This block will be invoked
+/// each time the bound stream is re-evaluated. This block must not be
+/// nil or return nil.
+///
+/// Returns a new stream which represents the combined result of all lazy
+/// applications of `block`.
+- (instancetype)bind:(RACStreamBindBlock (^)(void))block;
+
+/// Appends the values of `stream` to the values in the receiver.
+///
+/// stream - A stream to concatenate. This must be an instance of the same
+/// concrete class as the receiver, and should not be `nil`.
+///
+/// Returns a new stream representing the receiver followed by `stream`.
+- (instancetype)concat:(RACStream *)stream;
+
+/// Zips the values in the receiver with those of the given stream to create
+/// RACTuples.
+///
+/// The first value of each stream will be combined, then the second value, and
+/// so forth, until at least one of the streams is exhausted.
+///
+/// stream - The stream to zip with. This must be an instance of the same
+/// concrete class as the receiver, and should not be `nil`.
+///
+/// Returns a new stream of RACTuples, representing the zipped values of the
+/// two streams.
+- (instancetype)zipWith:(RACStream *)stream;
+
+@end
+
+/// This extension contains functionality to support naming streams for
+/// debugging.
+///
+/// Subclasses do not need to override the methods here.
+@interface RACStream ()
+
+/// The name of the stream. This is for debugging/human purposes only.
+@property (copy) NSString *name;
+
+/// Sets the name of the receiver to the given format string.
+///
+/// This is for debugging purposes only, and won't do anything unless the
+/// RAC_DEBUG_SIGNAL_NAMES environment variable is set.
+///
+/// Returns the receiver, for easy method chaining.
+- (instancetype)setNameWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
+
+@end
+
+/// Operations built on the RACStream primitives.
+///
+/// These methods do not need to be overridden, although subclasses may
+/// occasionally gain better performance from doing so.
+@interface RACStream (Operations)
+
+/// Maps `block` across the values in the receiver and flattens the result.
+///
+/// Note that operators applied _after_ -flattenMap: behave differently from
+/// operators _within_ -flattenMap:. See the Examples section below.
+///
+/// This corresponds to the `SelectMany` method in Rx.
+///
+/// block - A block which accepts the values in the receiver and returns a new
+/// instance of the receiver's class. Returning `nil` from this block is
+/// equivalent to returning an empty signal.
+///
+/// Examples
+///
+/// [signal flattenMap:^(id x) {
+/// // Logs each time a returned signal completes.
+/// return [[RACSignal return:x] logCompleted];
+/// }];
+///
+/// [[signal
+/// flattenMap:^(id x) {
+/// return [RACSignal return:x];
+/// }]
+/// // Logs only once, when all of the signals complete.
+/// logCompleted];
+///
+/// Returns a new stream which represents the combined streams resulting from
+/// mapping `block`.
+- (instancetype)flattenMap:(RACStream * (^)(id value))block;
+
+/// Flattens a stream of streams.
+///
+/// This corresponds to the `Merge` method in Rx.
+///
+/// Returns a stream consisting of the combined streams obtained from the
+/// receiver.
+- (instancetype)flatten;
+
+/// 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;
+
+/// Replace each value in the receiver with the given object.
+///
+/// Returns a new stream which includes the given object once for each value in
+/// the receiver.
+- (instancetype)mapReplace:(id)object;
+
+/// Filters out values in the receiver that don't pass the given test.
+///
+/// This corresponds to the `Where` method in Rx.
+///
+/// Returns a new stream with only those values that passed.
+- (instancetype)filter:(BOOL (^)(id value))block;
+
+/// Filters out values in the receiver that equal (via -isEqual:) the provided value.
+///
+/// value - The value can be `nil`, in which case it ignores `nil` values.
+///
+/// Returns a new stream containing only the values which did not compare equal
+/// to `value`.
+- (instancetype)ignore:(id)value;
+
+/// Unpacks each RACTuple in the receiver and maps the values to a new value.
+///
+/// reduceBlock - The block which reduces each RACTuple's values into one value.
+/// It must take as many arguments as the number of tuple elements
+/// to process. Each argument will be an object argument. The
+/// return value must be an object. This argument cannot be nil.
+///
+/// Returns a new stream of reduced tuple values.
+- (instancetype)reduceEach:(id (^)())reduceBlock;
+
+/// Returns a stream consisting of `value`, followed by the values in the
+/// receiver.
+- (instancetype)startWith:(id)value;
+
+/// Skips the first `skipCount` values in the receiver.
+///
+/// Returns the receiver after skipping the first `skipCount` values. If
+/// `skipCount` is greater than the number of values in the stream, an empty
+/// stream is returned.
+- (instancetype)skip:(NSUInteger)skipCount;
+
+/// Returns a stream of the first `count` values in the receiver. If `count` is
+/// greater than or equal to the number of values in the stream, a stream
+/// equivalent to the receiver is returned.
+- (instancetype)take:(NSUInteger)count;
+
+/// Zips the values in the given streams to create RACTuples.
+///
+/// The first value of each stream will be combined, then the second value, and
+/// so forth, until at least one of the streams is exhausted.
+///
+/// streams - The streams to combine. These must all be instances of the same
+/// concrete class implementing the protocol. If this collection is
+/// empty, the returned stream will be empty.
+///
+/// Returns a new stream containing RACTuples of the zipped values from the
+/// streams.
++ (instancetype)zip:(id<NSFastEnumeration>)streams;
+
+/// Zips streams using +zip:, then reduces the resulting tuples into a single
+/// value using -reduceEach:
+///
+/// streams - The streams to combine. These must all be instances of the
+/// same concrete class implementing the protocol. If this
+/// collection is empty, the returned stream will be empty.
+/// reduceBlock - The block which reduces the values from all the streams
+/// into one value. It must take as many arguments as the
+/// number of streams given. Each argument will be an object
+/// argument. The return value must be an object. This argument
+/// must not be nil.
+///
+/// Example:
+///
+/// [RACStream zip:@[ stringSignal, intSignal ] reduce:^(NSString *string, NSNumber *number) {
+/// return [NSString stringWithFormat:@"%@: %@", string, number];
+/// }];
+///
+/// Returns a new stream containing the results from each invocation of
+/// `reduceBlock`.
++ (instancetype)zip:(id<NSFastEnumeration>)streams reduce:(id (^)())reduceBlock;
+
+/// Returns a stream obtained by concatenating `streams` in order.
++ (instancetype)concat:(id<NSFastEnumeration>)streams;
+
+/// Combines values in the receiver from left to right using the given block.
+///
+/// The algorithm proceeds as follows:
+///
+/// 1. `startingValue` is passed into the block as the `running` value, and the
+/// first element of the receiver is passed into the block as the `next` value.
+/// 2. The result of the invocation is added to the returned stream.
+/// 3. The result of the invocation (`running`) and the next element of the
+/// receiver (`next`) is passed into `block`.
+/// 4. Steps 2 and 3 are repeated until all values have been processed.
+///
+/// startingValue - The value to be combined with the first element of the
+/// receiver. This value may be `nil`.
+/// block - A block that describes how to combine values of the
+/// receiver. If the receiver is empty, this block will never be
+/// invoked.
+///
+/// Examples
+///
+/// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence;
+///
+/// // Contains 1, 3, 6, 10
+/// RACSequence *sums = [numbers scanWithStart:@0 reduce:^(NSNumber *sum, NSNumber *next) {
+/// return @(sum.integerValue + next.integerValue);
+/// }];
+///
+/// Returns a new stream that consists of each application of `block`. If the
+/// receiver is empty, an empty stream is returned.
+- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))block;
+
+/// Combines each previous and current value into one object.
+///
+/// This method is similar to -scanWithStart:reduce:, but only ever operates on
+/// the previous and current values (instead of the whole stream), and does not
+/// pass the return value of `reduceBlock` into the next invocation of it.
+///
+/// start - The value passed into `reduceBlock` as `previous` for the
+/// first value.
+/// reduceBlock - The block that combines the previous value and the current
+/// value to create the reduced value. Cannot be nil.
+///
+/// Examples
+///
+/// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence;
+///
+/// // Contains 1, 3, 5, 7
+/// RACSequence *sums = [numbers combinePreviousWithStart:@0 reduce:^(NSNumber *previous, NSNumber *next) {
+/// return @(previous.integerValue + next.integerValue);
+/// }];
+///
+/// Returns a new stream consisting of the return values from each application of
+/// `reduceBlock`.
+- (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id current))reduceBlock;
+
+/// Takes values until the given block returns `YES`.
+///
+/// Returns a stream of the initial values in the receiver that fail `predicate`.
+/// If `predicate` never returns `YES`, a stream equivalent to the receiver is
+/// returned.
+- (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate;
+
+/// Takes values until the given block returns `NO`.
+///
+/// Returns a stream of the initial values in the receiver that pass `predicate`.
+/// If `predicate` never returns `NO`, a stream equivalent to the receiver is
+/// returned.
+- (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate;
+
+/// Skips values until the given block returns `YES`.
+///
+/// Returns a stream containing the values of the receiver that follow any
+/// initial values failing `predicate`. If `predicate` never returns `YES`,
+/// an empty stream is returned.
+- (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate;
+
+/// Skips values until the given block returns `NO`.
+///
+/// Returns a stream containing the values of the receiver that follow any
+/// initial values passing `predicate`. If `predicate` never returns `NO`, an
+/// empty stream is returned.
+- (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate;
+
+/// Returns a stream of values for which -isEqual: returns NO when compared to the
+/// previous value.
+- (instancetype)distinctUntilChanged;
+
+@end
+
+@interface RACStream (Deprecated)
+
+- (instancetype)sequenceMany:(RACStream * (^)(void))block __attribute__((deprecated("Use -flattenMap: instead")));
+- (instancetype)scanWithStart:(id)startingValue combine:(id (^)(id running, id next))block __attribute__((deprecated("Renamed to -scanWithStart:reduce:")));
+- (instancetype)mapPreviousWithStart:(id)start reduce:(id (^)(id previous, id current))combineBlock __attribute__((deprecated("Renamed to -combinePreviousWithStart:reduce:")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACStream.m b/ReactiveCocoaFramework/ReactiveCocoa/RACStream.m
new file mode 100644
index 0000000..7c37c69
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACStream.m
@@ -0,0 +1,362 @@
+//
+// RACStream.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-31.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACStream.h"
+#import "NSObject+RACDescription.h"
+#import "RACBlockTrampoline.h"
+#import "RACTuple.h"
+
+@implementation RACStream
+
+#pragma mark Lifecycle
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ self.name = @"";
+ return self;
+}
+
+#pragma mark Abstract methods
+
++ (instancetype)empty {
+ return nil;
+}
+
+- (instancetype)bind:(RACStreamBindBlock (^)(void))block {
+ return nil;
+}
+
++ (instancetype)return:(id)value {
+ return nil;
+}
+
+- (instancetype)concat:(RACStream *)stream {
+ return nil;
+}
+
+- (instancetype)zipWith:(RACStream *)stream {
+ return nil;
+}
+
+#pragma mark Naming
+
+- (instancetype)setNameWithFormat:(NSString *)format, ... {
+ if (getenv("RAC_DEBUG_SIGNAL_NAMES") == NULL) return self;
+
+ NSCParameterAssert(format != nil);
+
+ va_list args;
+ va_start(args, format);
+
+ NSString *str = [[NSString alloc] initWithFormat:format arguments:args];
+ va_end(args);
+
+ self.name = str;
+ return self;
+}
+
+@end
+
+@implementation RACStream (Operations)
+
+- (instancetype)flattenMap:(RACStream * (^)(id value))block {
+ Class class = self.class;
+
+ return [[self bind:^{
+ return ^(id value, BOOL *stop) {
+ id stream = block(value) ?: [class empty];
+ NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
+
+ return stream;
+ };
+ }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
+}
+
+- (instancetype)flatten {
+ __weak RACStream *stream __attribute__((unused)) = self;
+ return [[self flattenMap:^(id value) {
+ return value;
+ }] setNameWithFormat:@"[%@] -flatten", self.name];
+}
+
+- (instancetype)map:(id (^)(id value))block {
+ NSCParameterAssert(block != nil);
+
+ Class class = self.class;
+
+ return [[self flattenMap:^(id value) {
+ return [class return:block(value)];
+ }] setNameWithFormat:@"[%@] -map:", self.name];
+}
+
+- (instancetype)mapReplace:(id)object {
+ return [[self map:^(id _) {
+ return object;
+ }] setNameWithFormat:@"[%@] -mapReplace: %@", self.name, [object rac_description]];
+}
+
+- (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id next))reduceBlock {
+ NSCParameterAssert(reduceBlock != NULL);
+ return [[[self
+ scanWithStart:[RACTuple tupleWithObjects:start, nil]
+ reduce:^(RACTuple *previousTuple, id next) {
+ id value = reduceBlock(previousTuple[0], next);
+ return [RACTuple tupleWithObjects:next ?: RACTupleNil.tupleNil, value ?: RACTupleNil.tupleNil, nil];
+ }]
+ map:^(RACTuple *tuple) {
+ return tuple[1];
+ }]
+ setNameWithFormat:@"[%@] -combinePreviousWithStart: %@ reduce:", self.name, [start rac_description]];
+}
+
+- (instancetype)filter:(BOOL (^)(id value))block {
+ NSCParameterAssert(block != nil);
+
+ Class class = self.class;
+
+ return [[self flattenMap:^ id (id value) {
+ if (block(value)) {
+ return [class return:value];
+ } else {
+ return class.empty;
+ }
+ }] setNameWithFormat:@"[%@] -filter:", self.name];
+}
+
+- (instancetype)ignore:(id)value {
+ return [[self filter:^ BOOL (id innerValue) {
+ return innerValue != value && ![innerValue isEqual:value];
+ }] setNameWithFormat:@"[%@] -ignore: %@", self.name, [value rac_description]];
+}
+
+- (instancetype)reduceEach:(id (^)())reduceBlock {
+ NSCParameterAssert(reduceBlock != nil);
+
+ __weak RACStream *stream __attribute__((unused)) = self;
+ return [[self map:^(RACTuple *t) {
+ NSCAssert([t isKindOfClass:RACTuple.class], @"Value from stream %@ is not a tuple: %@", stream, t);
+ return [RACBlockTrampoline invokeBlock:reduceBlock withArguments:t];
+ }] setNameWithFormat:@"[%@] -reduceEach:", self.name];
+}
+
+- (instancetype)startWith:(id)value {
+ return [[[self.class return:value]
+ concat:self]
+ setNameWithFormat:@"[%@] -startWith: %@", self.name, [value rac_description]];
+}
+
+- (instancetype)skip:(NSUInteger)skipCount {
+ Class class = self.class;
+
+ return [[self bind:^{
+ __block NSUInteger skipped = 0;
+
+ return ^(id value, BOOL *stop) {
+ if (skipped >= skipCount) return [class return:value];
+
+ skipped++;
+ return class.empty;
+ };
+ }] setNameWithFormat:@"[%@] -skip: %lu", self.name, (unsigned long)skipCount];
+}
+
+- (instancetype)take:(NSUInteger)count {
+ Class class = self.class;
+
+ return [[self bind:^{
+ __block NSUInteger taken = 0;
+
+ return ^ id (id value, BOOL *stop) {
+ RACStream *result = class.empty;
+
+ if (taken < count) result = [class return:value];
+ if (++taken >= count) *stop = YES;
+
+ return result;
+ };
+ }] setNameWithFormat:@"[%@] -take: %lu", self.name, (unsigned long)count];
+}
+
++ (instancetype)join:(id<NSFastEnumeration>)streams block:(RACStream * (^)(id, id))block {
+ RACStream *current = nil;
+
+ // Creates streams of successively larger tuples by combining the input
+ // streams one-by-one.
+ for (RACStream *stream in streams) {
+ // For the first stream, just wrap its values in a RACTuple. That way,
+ // if only one stream is given, the result is still a stream of tuples.
+ if (current == nil) {
+ current = [stream map:^(id x) {
+ return RACTuplePack(x);
+ }];
+
+ continue;
+ }
+
+ current = block(current, stream);
+ }
+
+ if (current == nil) return [self empty];
+
+ return [current map:^(RACTuple *xs) {
+ // Right now, each value is contained in its own tuple, sorta like:
+ //
+ // (((1), 2), 3)
+ //
+ // We need to unwrap all the layers and create a tuple out of the result.
+ NSMutableArray *values = [[NSMutableArray alloc] init];
+
+ while (xs != nil) {
+ [values insertObject:xs.last ?: RACTupleNil.tupleNil atIndex:0];
+ xs = (xs.count > 1 ? xs.first : nil);
+ }
+
+ return [RACTuple tupleWithObjectsFromArray:values];
+ }];
+}
+
++ (instancetype)zip:(id<NSFastEnumeration>)streams {
+ return [[self join:streams block:^(RACStream *left, RACStream *right) {
+ return [left zipWith:right];
+ }] setNameWithFormat:@"+zip: %@", streams];
+}
+
++ (instancetype)zip:(id<NSFastEnumeration>)streams reduce:(id (^)())reduceBlock {
+ NSCParameterAssert(reduceBlock != nil);
+
+ RACStream *result = [self zip:streams];
+
+ // Although we assert this condition above, older versions of this method
+ // supported this argument being nil. Avoid crashing Release builds of
+ // apps that depended on that.
+ if (reduceBlock != nil) result = [result reduceEach:reduceBlock];
+
+ return [result setNameWithFormat:@"+zip: %@ reduce:", streams];
+}
+
++ (instancetype)concat:(id<NSFastEnumeration>)streams {
+ RACStream *result = self.empty;
+ for (RACStream *stream in streams) {
+ result = [result concat:stream];
+ }
+
+ return [result setNameWithFormat:@"+concat: %@", streams];
+}
+
+- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))block {
+ NSCParameterAssert(block != nil);
+
+ Class class = self.class;
+
+ return [[self bind:^{
+ __block id running = startingValue;
+
+ return ^(id value, BOOL *stop) {
+ running = block(running, value);
+ return [class return:running];
+ };
+ }] setNameWithFormat:@"[%@] -scanWithStart: %@ reduce:", self.name, [startingValue rac_description]];
+}
+
+- (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate {
+ NSCParameterAssert(predicate != nil);
+
+ Class class = self.class;
+
+ return [[self bind:^{
+ return ^ id (id value, BOOL *stop) {
+ if (predicate(value)) return nil;
+
+ return [class return:value];
+ };
+ }] setNameWithFormat:@"[%@] -takeUntilBlock:", self.name];
+}
+
+- (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate {
+ NSCParameterAssert(predicate != nil);
+
+ return [[self takeUntilBlock:^ BOOL (id x) {
+ return !predicate(x);
+ }] setNameWithFormat:@"[%@] -takeWhileBlock:", self.name];
+}
+
+- (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate {
+ NSCParameterAssert(predicate != nil);
+
+ Class class = self.class;
+
+ return [[self bind:^{
+ __block BOOL skipping = YES;
+
+ return ^ id (id value, BOOL *stop) {
+ if (skipping) {
+ if (predicate(value)) {
+ skipping = NO;
+ } else {
+ return class.empty;
+ }
+ }
+
+ return [class return:value];
+ };
+ }] setNameWithFormat:@"[%@] -skipUntilBlock:", self.name];
+}
+
+- (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate {
+ NSCParameterAssert(predicate != nil);
+
+ return [[self skipUntilBlock:^ BOOL (id x) {
+ return !predicate(x);
+ }] setNameWithFormat:@"[%@] -skipUntilBlock:", self.name];
+}
+
+- (instancetype)distinctUntilChanged {
+ Class class = self.class;
+
+ return [[self bind:^{
+ __block id lastValue = nil;
+ __block BOOL initial = YES;
+
+ return ^(id x, BOOL *stop) {
+ if (!initial && (lastValue == x || [x isEqual:lastValue])) return [class empty];
+
+ initial = NO;
+ lastValue = x;
+ return [class return:x];
+ };
+ }] setNameWithFormat:@"[%@] -distinctUntilChanged", self.name];
+}
+
+@end
+
+@implementation RACStream (Deprecated)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+- (instancetype)sequenceMany:(RACStream * (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ return [[self flattenMap:^(id _) {
+ return block();
+ }] setNameWithFormat:@"[%@] -sequenceMany:", self.name];
+}
+
+- (instancetype)scanWithStart:(id)startingValue combine:(id (^)(id running, id next))block {
+ return [self scanWithStart:startingValue reduce:block];
+}
+
+- (instancetype)mapPreviousWithStart:(id)start reduce:(id (^)(id previous, id current))combineBlock {
+ return [self combinePreviousWithStart:start reduce:combineBlock];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.h
new file mode 100644
index 0000000..b37ddea
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.h
@@ -0,0 +1,18 @@
+//
+// RACStringSequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class that adapts a string to the RACSequence interface.
+@interface RACStringSequence : RACSequence
+
+// Returns a sequence for enumerating over the given string, starting from the
+// given character offset. The string will be copied to prevent mutation.
++ (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.m
new file mode 100644
index 0000000..dd10f3c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.m
@@ -0,0 +1,65 @@
+//
+// RACStringSequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-10-29.
+// Copyright (c) 2012 GitHub. All rights reserved.
+//
+
+#import "RACStringSequence.h"
+
+@interface RACStringSequence ()
+
+// The string being sequenced.
+@property (nonatomic, copy, readonly) NSString *string;
+
+// The index in the string from which the sequence starts.
+@property (nonatomic, assign, readonly) NSUInteger offset;
+
+@end
+
+@implementation RACStringSequence
+
+#pragma mark Lifecycle
+
++ (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset {
+ NSCParameterAssert(offset <= string.length);
+
+ if (offset == string.length) return self.empty;
+
+ RACStringSequence *seq = [[self alloc] init];
+ seq->_string = [string copy];
+ seq->_offset = offset;
+ return seq;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ return [self.string substringWithRange:NSMakeRange(self.offset, 1)];
+}
+
+- (RACSequence *)tail {
+ RACSequence *sequence = [self.class sequenceWithString:self.string offset:self.offset + 1];
+ sequence.name = self.name;
+ return sequence;
+}
+
+- (NSArray *)array {
+ NSUInteger substringLength = self.string.length - self.offset;
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:substringLength];
+
+ [self.string enumerateSubstringsInRange:NSMakeRange(self.offset, substringLength) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
+ [array addObject:substring];
+ }];
+
+ return [array copy];
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, string = %@ }", self.class, self, self.name, [self.string substringFromIndex:self.offset]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.h
new file mode 100644
index 0000000..30c100b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.h
@@ -0,0 +1,22 @@
+//
+// RACSubject.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/9/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+
+/// A subject can be thought of as a signal that you can manually control by
+/// sending next, completed, and error.
+///
+/// They're most helpful in bridging the non-RAC world to RAC, since they let you
+/// manually control the sending of events.
+@interface RACSubject : RACSignal <RACSubscriber>
+
+/// Returns a new subject.
++ (instancetype)subject;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.m
new file mode 100644
index 0000000..fa9ba7d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubject.m
@@ -0,0 +1,124 @@
+//
+// RACSubject.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/9/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubject.h"
+#import "EXTScope.h"
+#import "RACCompoundDisposable.h"
+#import "RACPassthroughSubscriber.h"
+
+@interface RACSubject ()
+
+// Contains all current subscribers to the receiver.
+//
+// This should only be used while synchronized on `self`.
+@property (nonatomic, strong, readonly) NSMutableArray *subscribers;
+
+// Contains all of the receiver's subscriptions to other signals.
+@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
+
+// Enumerates over each of the receiver's `subscribers` and invokes `block` for
+// each.
+- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block;
+
+@end
+
+@implementation RACSubject
+
+#pragma mark Lifecycle
+
++ (instancetype)subject {
+ return [[self alloc] init];
+}
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ _disposable = [RACCompoundDisposable compoundDisposable];
+ _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
+
+ return self;
+}
+
+- (void)dealloc {
+ [self.disposable dispose];
+}
+
+#pragma mark Subscription
+
+- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
+ NSCParameterAssert(subscriber != nil);
+
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+ subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
+
+ NSMutableArray *subscribers = self.subscribers;
+ @synchronized (subscribers) {
+ [subscribers addObject:subscriber];
+ }
+
+ return [RACDisposable disposableWithBlock:^{
+ @synchronized (subscribers) {
+ // Since newer subscribers are generally shorter-lived, search
+ // starting from the end of the list.
+ NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
+ return obj == subscriber;
+ }];
+
+ if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
+ }
+ }];
+}
+
+- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
+ NSArray *subscribers;
+ @synchronized (self.subscribers) {
+ subscribers = [self.subscribers copy];
+ }
+
+ for (id<RACSubscriber> subscriber in subscribers) {
+ block(subscriber);
+ }
+}
+
+#pragma mark RACSubscriber
+
+- (void)sendNext:(id)value {
+ [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
+ [subscriber sendNext:value];
+ }];
+}
+
+- (void)sendError:(NSError *)error {
+ [self.disposable dispose];
+
+ [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
+ [subscriber sendError:error];
+ }];
+}
+
+- (void)sendCompleted {
+ [self.disposable dispose];
+
+ [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
+ [subscriber sendCompleted];
+ }];
+}
+
+- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)d {
+ if (d.disposed) return;
+ [self.disposable addDisposable:d];
+
+ @weakify(self, d);
+ [d addDisposable:[RACDisposable disposableWithBlock:^{
+ @strongify(self, d);
+ [self.disposable removeDisposable:d];
+ }]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber+Private.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber+Private.h
new file mode 100644
index 0000000..0ad81d2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber+Private.h
@@ -0,0 +1,17 @@
+//
+// RACSubscriber+Private.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriber.h"
+
+// A simple block-based subscriber.
+@interface RACSubscriber : NSObject <RACSubscriber>
+
+// Creates a new subscriber with the given blocks.
++ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h
new file mode 100644
index 0000000..173e6dc
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h
@@ -0,0 +1,51 @@
+//
+// RACSubscriber.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/1/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class RACCompoundDisposable;
+
+/// Represents any object which can directly receive values from a RACSignal.
+///
+/// You generally shouldn't need to implement this protocol. +[RACSignal
+/// createSignal:], RACSignal's subscription methods, or RACSubject should work
+/// for most uses.
+///
+/// Implementors of this protocol may receive messages and values from multiple
+/// threads simultaneously, and so should be thread-safe. Subscribers will also
+/// be weakly referenced so implementations must allow that.
+@protocol RACSubscriber <NSObject>
+@required
+
+/// Send the next value to subscribers.
+///
+/// value - The value to send. This can be `nil`.
+- (void)sendNext:(id)value;
+
+/// Send the error to subscribers.
+///
+/// error - The error to send. This can be `nil`.
+///
+/// This terminates the subscription, and invalidates the subscriber (such that
+/// it cannot subscribe to anything else in the future).
+- (void)sendError:(NSError *)error;
+
+/// Send completed to subscribers.
+///
+/// This terminates the subscription, and invalidates the subscriber (such that
+/// it cannot subscribe to anything else in the future).
+- (void)sendCompleted;
+
+/// Sends the subscriber a disposable that represents one of its subscriptions.
+///
+/// A subscriber may receive multiple disposables if it gets subscribed to
+/// multiple signals; however, any error or completed events must terminate _all_
+/// subscriptions.
+- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.m
new file mode 100644
index 0000000..b9e83f0
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.m
@@ -0,0 +1,108 @@
+//
+// RACSubscriber.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/1/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriber.h"
+#import "RACSubscriber+Private.h"
+#import "EXTScope.h"
+#import "RACCompoundDisposable.h"
+
+@interface RACSubscriber ()
+
+// These callbacks should only be accessed while synchronized on self.
+@property (nonatomic, copy) void (^next)(id value);
+@property (nonatomic, copy) void (^error)(NSError *error);
+@property (nonatomic, copy) void (^completed)(void);
+
+@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
+
+@end
+
+@implementation RACSubscriber
+
+#pragma mark Lifecycle
+
++ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
+ RACSubscriber *subscriber = [[self alloc] init];
+
+ subscriber->_next = [next copy];
+ subscriber->_error = [error copy];
+ subscriber->_completed = [completed copy];
+
+ return subscriber;
+}
+
+- (id)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ @weakify(self);
+
+ RACDisposable *selfDisposable = [RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ if (self == nil) return;
+
+ @synchronized (self) {
+ self.next = nil;
+ self.error = nil;
+ self.completed = nil;
+ }
+ }];
+
+ _disposable = [RACCompoundDisposable compoundDisposable];
+ [_disposable addDisposable:selfDisposable];
+
+ return self;
+}
+
+- (void)dealloc {
+ [self.disposable dispose];
+}
+
+#pragma mark RACSubscriber
+
+- (void)sendNext:(id)value {
+ @synchronized (self) {
+ void (^nextBlock)(id) = [self.next copy];
+ if (nextBlock == nil) return;
+
+ nextBlock(value);
+ }
+}
+
+- (void)sendError:(NSError *)e {
+ @synchronized (self) {
+ void (^errorBlock)(NSError *) = [self.error copy];
+ [self.disposable dispose];
+
+ if (errorBlock == nil) return;
+ errorBlock(e);
+ }
+}
+
+- (void)sendCompleted {
+ @synchronized (self) {
+ void (^completedBlock)(void) = [self.completed copy];
+ [self.disposable dispose];
+
+ if (completedBlock == nil) return;
+ completedBlock();
+ }
+}
+
+- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)d {
+ if (d.disposed) return;
+ [self.disposable addDisposable:d];
+
+ @weakify(self, d);
+ [d addDisposable:[RACDisposable disposableWithBlock:^{
+ @strongify(self, d);
+ [self.disposable removeDisposable:d];
+ }]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h
new file mode 100644
index 0000000..8987731
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h
@@ -0,0 +1,54 @@
+//
+// RACSubscriptingAssignmentTrampoline.h
+// iOSDemo
+//
+// Created by Josh Abernathy on 9/24/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "EXTKeyPathCoding.h"
+
+@class RACSignal;
+
+/// Assigns a signal to an object property, automatically setting the given key
+/// path on every `next`. When the signal completes, the binding is automatically
+/// disposed of.
+///
+/// There are two different versions of this macro:
+///
+/// - RAC(TARGET, KEYPATH, NILVALUE) will bind the `KEYPATH` of `TARGET` to the
+/// given signal. If the signal ever sends a `nil` value, the property will be
+/// set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object
+/// properties, but an NSValue should be used for primitive properties, to
+/// avoid an exception if `nil` is sent (which might occur if an intermediate
+/// object is set to `nil`).
+/// - RAC(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to
+/// `nil`.
+///
+/// See -[RACSignal setKeyPath:onObject:nilValue:] for more information about the
+/// binding's semantics.
+///
+/// Examples
+///
+/// RAC(self, objectProperty) = objectSignal;
+/// RAC(self, stringProperty, @"foobar") = stringSignal;
+/// RAC(self, integerProperty, @42) = integerSignal;
+///
+/// WARNING: Under certain conditions, use of this macro can be thread-unsafe.
+/// See the documentation of -setKeyPath:onObject:nilValue:.
+#define RAC(TARGET, ...) \
+ metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
+ (RAC_(TARGET, __VA_ARGS__, nil)) \
+ (RAC_(TARGET, __VA_ARGS__))
+
+/// Do not use this directly. Use the RAC macro above.
+#define RAC_(TARGET, KEYPATH, NILVALUE) \
+ [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]
+
+@interface RACSubscriptingAssignmentTrampoline : NSObject
+
+- (id)initWithTarget:(id)target nilValue:(id)nilValue;
+- (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.m
new file mode 100644
index 0000000..2ab8cd3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.m
@@ -0,0 +1,42 @@
+//
+// RACSubscriptingAssignmentTrampoline.m
+// iOSDemo
+//
+// Created by Josh Abernathy on 9/24/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriptingAssignmentTrampoline.h"
+#import "RACSignal+Operations.h"
+
+@interface RACSubscriptingAssignmentTrampoline ()
+
+// The object to bind to.
+@property (nonatomic, strong, readonly) id target;
+
+// A value to use when `nil` is sent on the bound signal.
+@property (nonatomic, strong, readonly) id nilValue;
+
+@end
+
+@implementation RACSubscriptingAssignmentTrampoline
+
+- (id)initWithTarget:(id)target nilValue:(id)nilValue {
+ // This is often a programmer error, but this prevents crashes if the target
+ // object has unexpectedly deallocated.
+ if (target == nil) return nil;
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _target = target;
+ _nilValue = nilValue;
+
+ return self;
+}
+
+- (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath {
+ [signal setKeyPath:keyPath onObject:self.target nilValue:self.nilValue];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.h
new file mode 100644
index 0000000..34f5dc5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.h
@@ -0,0 +1,15 @@
+//
+// RACSubscriptionScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+
+// A private scheduler used only for subscriptions. See the private
+// +[RACScheduler subscriptionScheduler] method for more information.
+@interface RACSubscriptionScheduler : RACScheduler
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.m
new file mode 100644
index 0000000..2aab9c3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.m
@@ -0,0 +1,54 @@
+//
+// RACSubscriptionScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriptionScheduler.h"
+#import "RACScheduler+Private.h"
+
+@interface RACSubscriptionScheduler ()
+
+// A private background scheduler on which to subscribe if the +currentScheduler
+// is unknown.
+@property (nonatomic, strong, readonly) RACScheduler *backgroundScheduler;
+
+@end
+
+@implementation RACSubscriptionScheduler
+
+#pragma mark Lifecycle
+
+- (id)init {
+ self = [super initWithName:@"com.ReactiveCocoa.RACScheduler.subscriptionScheduler"];
+ if (self == nil) return nil;
+
+ _backgroundScheduler = [RACScheduler scheduler];
+
+ return self;
+}
+
+#pragma mark RACScheduler
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ NSCParameterAssert(block != NULL);
+
+ if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];
+
+ block();
+ return nil;
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler;
+ return [scheduler after:date schedule:block];
+}
+
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
+ RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler;
+ return [scheduler after:date repeatingEvery:interval withLeeway:leeway schedule:block];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.h
new file mode 100644
index 0000000..429e595
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.h
@@ -0,0 +1,24 @@
+//
+// RACTargetQueueScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/6/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACQueueScheduler.h"
+
+/// A scheduler that enqueues blocks on a private serial queue, targeting an
+/// arbitrary GCD queue.
+@interface RACTargetQueueScheduler : RACQueueScheduler
+
+/// Initializes the receiver with a serial queue that will target the given
+/// `targetQueue`.
+///
+/// name - The name of the scheduler. If nil, a default name will be used.
+/// targetQueue - The queue to target. Cannot be NULL.
+///
+/// Returns the initialized object.
+- (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.m
new file mode 100644
index 0000000..65114d3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTargetQueueScheduler.m
@@ -0,0 +1,37 @@
+//
+// RACTargetQueueScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/6/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTargetQueueScheduler.h"
+#import "RACBacktrace.h"
+#import "RACQueueScheduler+Subclass.h"
+
+@implementation RACTargetQueueScheduler
+
+#pragma mark Lifecycle
+
+- (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue {
+ NSCParameterAssert(targetQueue != NULL);
+
+ if (name == nil) {
+ name = [NSString stringWithFormat:@"com.ReactiveCocoa.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)];
+ }
+
+ dispatch_queue_t queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL);
+ if (queue == NULL) return nil;
+
+ dispatch_set_target_queue(queue, targetQueue);
+
+ self = [super initWithName:name queue:queue];
+ if (self == nil) return nil;
+
+ dispatch_release(queue);
+
+ return self;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.h b/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.h
new file mode 100644
index 0000000..a790f5b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.h
@@ -0,0 +1,42 @@
+//
+// RACTestScheduler.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+
+/// A special kind of scheduler that steps through virtualized time.
+///
+/// This scheduler class can be used in unit tests to verify asynchronous
+/// behaviors without spending significant time waiting.
+///
+/// This class can be used from multiple threads, but only one thread can `step`
+/// through the enqueued actions at a time. Other threads will wait while the
+/// scheduled blocks are being executed.
+@interface RACTestScheduler : RACScheduler
+
+/// Initializes a new test scheduler.
+- (instancetype)init;
+
+/// Executes the next scheduled block, if any.
+///
+/// This method will block until the scheduled action has completed.
+- (void)step;
+
+/// Executes up to the next `ticks` scheduled blocks.
+///
+/// This method will block until the scheduled actions have completed.
+///
+/// ticks - The number of scheduled blocks to execute. If there aren't this many
+/// blocks enqueued, all scheduled blocks are executed.
+- (void)step:(NSUInteger)ticks;
+
+/// Executes all of the scheduled blocks on the receiver.
+///
+/// This method will block until the scheduled actions have completed.
+- (void)stepAll;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.m b/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.m
new file mode 100644
index 0000000..b6997b2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTestScheduler.m
@@ -0,0 +1,223 @@
+//
+// RACTestScheduler.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestScheduler.h"
+#import "EXTScope.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACScheduler+Private.h"
+
+@interface RACTestSchedulerAction : NSObject
+
+// The date at which the action should be executed.
+//
+// This absolute time will not actually be honored. This date is only used for
+// comparison, to determine which block should be run _next_.
+@property (nonatomic, copy, readonly) NSDate *date;
+
+// The scheduled block.
+@property (nonatomic, copy, readonly) void (^block)(void);
+
+// A disposable for this action.
+//
+// When disposed, the action should not start executing if it hasn't already.
+@property (nonatomic, strong, readonly) RACDisposable *disposable;
+
+// Initializes a new scheduler action.
+- (id)initWithDate:(NSDate *)date block:(void (^)(void))block;
+
+@end
+
+static CFComparisonResult RACCompareScheduledActions(const void *ptr1, const void *ptr2, void *info) {
+ RACTestSchedulerAction *action1 = (__bridge id)ptr1;
+ RACTestSchedulerAction *action2 = (__bridge id)ptr2;
+ return CFDateCompare((__bridge CFDateRef)action1.date, (__bridge CFDateRef)action2.date, NULL);
+}
+
+static const void *RACRetainScheduledAction(CFAllocatorRef allocator, const void *ptr) {
+ return CFRetain(ptr);
+}
+
+static void RACReleaseScheduledAction(CFAllocatorRef allocator, const void *ptr) {
+ CFRelease(ptr);
+}
+
+@interface RACTestScheduler ()
+
+// All of the RACTestSchedulerActions that have been enqueued and not yet
+// executed.
+//
+// The minimum value in the heap represents the action to execute next.
+//
+// This property should only be used while synchronized on self.
+@property (nonatomic, assign, readonly) CFBinaryHeapRef scheduledActions;
+
+// The number of blocks that have been directly enqueued with -schedule: so
+// far.
+//
+// This is used to ensure unique dates when two blocks are enqueued
+// simultaneously.
+//
+// This property should only be used while synchronized on self.
+@property (nonatomic, assign) NSUInteger numberOfDirectlyScheduledBlocks;
+
+@end
+
+@implementation RACTestScheduler
+
+#pragma mark Lifecycle
+
+- (instancetype)init {
+ self = [super initWithName:@"com.github.ReactiveCocoa.RACTestScheduler"];
+ if (self == nil) return nil;
+
+ CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks){
+ .version = 0,
+ .retain = &RACRetainScheduledAction,
+ .release = &RACReleaseScheduledAction,
+ .copyDescription = &CFCopyDescription,
+ .compare = &RACCompareScheduledActions
+ };
+
+ _scheduledActions = CFBinaryHeapCreate(NULL, 0, &callbacks, NULL);
+ return self;
+}
+
+- (void)dealloc {
+ [self stepAll];
+
+ if (_scheduledActions != NULL) {
+ CFRelease(_scheduledActions);
+ _scheduledActions = NULL;
+ }
+}
+
+#pragma mark Execution
+
+- (void)step {
+ [self step:1];
+}
+
+- (void)step:(NSUInteger)ticks {
+ @synchronized (self) {
+ for (NSUInteger i = 0; i < ticks; i++) {
+ const void *actionPtr = NULL;
+ if (!CFBinaryHeapGetMinimumIfPresent(self.scheduledActions, &actionPtr)) break;
+
+ RACTestSchedulerAction *action = (__bridge id)actionPtr;
+ CFBinaryHeapRemoveMinimumValue(self.scheduledActions);
+
+ if (action.disposable.disposed) continue;
+
+ RACScheduler *previousScheduler = RACScheduler.currentScheduler;
+ NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self;
+
+ action.block();
+
+ if (previousScheduler != nil) {
+ NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler;
+ } else {
+ [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey];
+ }
+ }
+ }
+}
+
+- (void)stepAll {
+ [self step:NSUIntegerMax];
+}
+
+#pragma mark RACScheduler
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ NSCParameterAssert(block != nil);
+
+ @synchronized (self) {
+ NSDate *uniqueDate = [NSDate dateWithTimeIntervalSinceReferenceDate:self.numberOfDirectlyScheduledBlocks];
+ self.numberOfDirectlyScheduledBlocks++;
+
+ RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:uniqueDate block:block];
+ CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action);
+
+ return action.disposable;
+ }
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(block != nil);
+
+ @synchronized (self) {
+ RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:date block:block];
+ CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action);
+
+ return action.disposable;
+ }
+}
+
+- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(block != nil);
+ NSCParameterAssert(interval >= 0);
+ NSCParameterAssert(leeway >= 0);
+
+ RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
+
+ @weakify(self);
+ @synchronized (self) {
+ __block RACDisposable *thisDisposable = nil;
+
+ void (^reschedulingBlock)(void) = ^{
+ @strongify(self);
+
+ [compoundDisposable removeDisposable:thisDisposable];
+
+ // Schedule the next interval.
+ RACDisposable *schedulingDisposable = [self after:[date dateByAddingTimeInterval:interval] repeatingEvery:interval withLeeway:leeway schedule:block];
+ [compoundDisposable addDisposable:schedulingDisposable];
+
+ block();
+ };
+
+ RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:date block:reschedulingBlock];
+ CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action);
+
+ thisDisposable = action.disposable;
+ [compoundDisposable addDisposable:thisDisposable];
+ }
+
+ return compoundDisposable;
+}
+
+@end
+
+@implementation RACTestSchedulerAction
+
+#pragma mark Lifecycle
+
+- (id)initWithDate:(NSDate *)date block:(void (^)(void))block {
+ NSCParameterAssert(date != nil);
+ NSCParameterAssert(block != nil);
+
+ self = [super init];
+ if (self == nil) return nil;
+
+ _date = [date copy];
+ _block = [block copy];
+ _disposable = [[RACDisposable alloc] init];
+
+ return self;
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ date: %@ }", self.class, self, self.date];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h
new file mode 100644
index 0000000..647b42c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h
@@ -0,0 +1,159 @@
+//
+// RACTuple.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/12/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "metamacros.h"
+
+@class RACSequence;
+
+/// Creates a new tuple with the given values. At least one value must be given.
+/// Values can be nil.
+#define RACTuplePack(...) \
+ RACTuplePack_(__VA_ARGS__)
+
+/// Declares new object variables and unpacks a RACTuple into them.
+///
+/// This macro should be used on the left side of an assignment, with the
+/// tuple on the right side. Nothing else should appear on the same line, and the
+/// macro should not be the only statement in a conditional or loop body.
+///
+/// If the tuple has more values than there are variables listed, the excess
+/// values are ignored.
+///
+/// If the tuple has fewer values than there are variables listed, the excess
+/// variables are initialized to nil.
+///
+/// Examples
+///
+/// RACTupleUnpack(NSString *string, NSNumber *num) = [RACTuple tupleWithObjects:@"foo", @5, nil];
+/// NSLog(@"string: %@", string);
+/// NSLog(@"num: %@", num);
+///
+/// /* The above is equivalent to: */
+/// RACTuple *t = [RACTuple tupleWithObjects:@"foo", @5, nil];
+/// NSString *string = t[0];
+/// NSNumber *num = t[1];
+/// NSLog(@"string: %@", string);
+/// NSLog(@"num: %@", num);
+#define RACTupleUnpack(...) \
+ RACTupleUnpack_(__VA_ARGS__)
+
+/// A sentinel object that represents nils in the tuple.
+///
+/// It should never be necessary to create a tuple nil yourself. Just use
+/// +tupleNil.
+@interface RACTupleNil : NSObject <NSCopying, NSCoding>
+/// A singleton instance.
++ (RACTupleNil *)tupleNil;
+@end
+
+
+/// A tuple is an ordered collection of objects. It may contain nils, represented
+/// by RACTupleNil.
+@interface RACTuple : NSObject <NSCoding, NSCopying, NSFastEnumeration>
+
+@property (nonatomic, readonly) NSUInteger count;
+
+/// These properties all return the object at that index or nil if the number of
+/// objects is less than the index.
+@property (nonatomic, readonly) id first;
+@property (nonatomic, readonly) id second;
+@property (nonatomic, readonly) id third;
+@property (nonatomic, readonly) id fourth;
+@property (nonatomic, readonly) id fifth;
+@property (nonatomic, readonly) id last;
+
+/// Creates a new tuple out of the array. Does not convert nulls to nils.
++ (instancetype)tupleWithObjectsFromArray:(NSArray *)array;
+
+/// Creates a new tuple out of the array. If `convert` is YES, it also converts
+/// every NSNull to RACTupleNil.
++ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert;
+
+/// Creates a new tuple with the given objects. Use RACTupleNil to represent
+/// nils.
++ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
+
+/// Returns the object at `index` or nil if the object is a RACTupleNil. Unlike
+/// NSArray and friends, it's perfectly fine to ask for the object at an index
+/// past the tuple's count - 1. It will simply return nil.
+- (id)objectAtIndex:(NSUInteger)index;
+
+/// Returns an array of all the objects. RACTupleNils are converted to NSNulls.
+- (NSArray *)allObjects;
+
+/// Appends `obj` to the receiver.
+///
+/// obj - The object to add to the tuple. This argument may be nil.
+///
+/// Returns a new tuple.
+- (instancetype)tupleByAddingObject:(id)obj;
+
+@end
+
+@interface RACTuple (RACSequenceAdditions)
+
+/// Returns a sequence of all the objects. RACTupleNils are converted to NSNulls.
+@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
+
+@end
+
+@interface RACTuple (ObjectSubscripting)
+/// Returns the object at that index or nil if the number of objects is less
+/// than the index.
+- (id)objectAtIndexedSubscript:(NSUInteger)idx;
+@end
+
+/// This and everything below is for internal use only.
+///
+/// See RACTuplePack() and RACTupleUnpack() instead.
+#define RACTuplePack_(...) \
+ ([RACTuple tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]])
+
+#define RACTuplePack_object_or_ractuplenil(INDEX, ARG) \
+ (ARG) ?: RACTupleNil.tupleNil,
+
+#define RACTupleUnpack_(...) \
+ metamacro_foreach(RACTupleUnpack_decl,, __VA_ARGS__) \
+ \
+ int RACTupleUnpack_state = 0; \
+ \
+ RACTupleUnpack_after: \
+ ; \
+ metamacro_foreach(RACTupleUnpack_assign,, __VA_ARGS__) \
+ if (RACTupleUnpack_state != 0) RACTupleUnpack_state = 2; \
+ \
+ while (RACTupleUnpack_state != 2) \
+ if (RACTupleUnpack_state == 1) { \
+ goto RACTupleUnpack_after; \
+ } else \
+ for (; RACTupleUnpack_state != 1; RACTupleUnpack_state = 1) \
+ [RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ]
+
+#define RACTupleUnpack_state metamacro_concat(RACTupleUnpack_state, __LINE__)
+#define RACTupleUnpack_after metamacro_concat(RACTupleUnpack_after, __LINE__)
+#define RACTupleUnpack_loop metamacro_concat(RACTupleUnpack_loop, __LINE__)
+
+#define RACTupleUnpack_decl_name(INDEX) \
+ metamacro_concat(metamacro_concat(RACTupleUnpack, __LINE__), metamacro_concat(_var, INDEX))
+
+#define RACTupleUnpack_decl(INDEX, ARG) \
+ __strong id RACTupleUnpack_decl_name(INDEX);
+
+#define RACTupleUnpack_assign(INDEX, ARG) \
+ __strong ARG = RACTupleUnpack_decl_name(INDEX);
+
+#define RACTupleUnpack_value(INDEX, ARG) \
+ [NSValue valueWithPointer:&RACTupleUnpack_decl_name(INDEX)],
+
+@interface RACTupleUnpackingTrampoline : NSObject
+
++ (instancetype)trampoline;
+- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m
new file mode 100644
index 0000000..9d338db
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m
@@ -0,0 +1,252 @@
+//
+// RACTuple.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/12/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTuple.h"
+#import "EXTKeyPathCoding.h"
+#import "RACTupleSequence.h"
+
+@implementation RACTupleNil
+
++ (RACTupleNil *)tupleNil {
+ static dispatch_once_t onceToken;
+ static RACTupleNil *tupleNil = nil;
+ dispatch_once(&onceToken, ^{
+ tupleNil = [[self alloc] init];
+ });
+
+ return tupleNil;
+}
+
+#pragma mark NSCopying
+
+- (id)copyWithZone:(NSZone *)zone {
+ return self;
+}
+
+#pragma mark NSCoding
+
+- (id)initWithCoder:(NSCoder *)coder {
+ // Always return the singleton.
+ return self.class.tupleNil;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+}
+
+@end
+
+
+@interface RACTuple ()
+@property (nonatomic, strong) NSArray *backingArray;
+@end
+
+
+@implementation RACTuple
+
+- (instancetype)init {
+ self = [super init];
+ if (self == nil) return nil;
+
+ self.backingArray = [NSArray array];
+
+ return self;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.allObjects];
+}
+
+- (BOOL)isEqual:(RACTuple *)object {
+ if (object == self) return YES;
+ if (![object isKindOfClass:self.class]) return NO;
+
+ return [self.backingArray isEqual:object.backingArray];
+}
+
+- (NSUInteger)hash {
+ return self.backingArray.hash;
+}
+
+
+#pragma mark NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
+ return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len];
+}
+
+
+#pragma mark NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ // we're immutable, bitches!
+ return self;
+}
+
+
+#pragma mark NSCoding
+
+- (id)initWithCoder:(NSCoder *)coder {
+ self = [self init];
+ if (self == nil) return nil;
+
+ self.backingArray = [coder decodeObjectForKey:@keypath(self.backingArray)];
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ if (self.backingArray != nil) [coder encodeObject:self.backingArray forKey:@keypath(self.backingArray)];
+}
+
+
+#pragma mark API
+
++ (instancetype)tupleWithObjectsFromArray:(NSArray *)array {
+ return [self tupleWithObjectsFromArray:array convertNullsToNils:NO];
+}
+
++ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert {
+ RACTuple *tuple = [[self alloc] init];
+
+ if (convert) {
+ NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
+ for (id object in array) {
+ [newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];
+ }
+
+ tuple.backingArray = newArray;
+ } else {
+ tuple.backingArray = [array copy];
+ }
+
+ return tuple;
+}
+
++ (instancetype)tupleWithObjects:(id)object, ... {
+ RACTuple *tuple = [[self alloc] init];
+
+ va_list args;
+ va_start(args, object);
+
+ NSUInteger count = 0;
+ for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {
+ ++count;
+ }
+
+ va_end(args);
+
+ if (count == 0) {
+ tuple.backingArray = @[];
+ return tuple;
+ }
+
+ NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:count];
+
+ va_start(args, object);
+ for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {
+ [objects addObject:currentObject];
+ }
+
+ va_end(args);
+
+ tuple.backingArray = objects;
+ return tuple;
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+ if (index >= self.count) return nil;
+
+ id object = [self.backingArray objectAtIndex:index];
+ return (object == RACTupleNil.tupleNil ? nil : object);
+}
+
+- (NSArray *)allObjects {
+ NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:self.backingArray.count];
+ for (id object in self.backingArray) {
+ [newArray addObject:(object == RACTupleNil.tupleNil ? NSNull.null : object)];
+ }
+
+ return newArray;
+}
+
+- (instancetype)tupleByAddingObject:(id)obj {
+ NSArray *newArray = [self.backingArray arrayByAddingObject:obj ?: RACTupleNil.tupleNil];
+ return [self.class tupleWithObjectsFromArray:newArray convertNullsToNils:NO];
+}
+
+- (NSUInteger)count {
+ return self.backingArray.count;
+}
+
+- (id)first {
+ return [self objectAtIndex:0];
+}
+
+- (id)second {
+ return [self objectAtIndex:1];
+}
+
+- (id)third {
+ return [self objectAtIndex:2];
+}
+
+- (id)fourth {
+ return [self objectAtIndex:3];
+}
+
+- (id)fifth {
+ return [self objectAtIndex:4];
+}
+
+- (id)last {
+ return [self objectAtIndex:self.count - 1];
+}
+
+@end
+
+
+@implementation RACTuple (RACSequenceAdditions)
+
+- (RACSequence *)rac_sequence {
+ return [RACTupleSequence sequenceWithTupleBackingArray:self.backingArray offset:0];
+}
+
+@end
+
+@implementation RACTuple (ObjectSubscripting)
+
+- (id)objectAtIndexedSubscript:(NSUInteger)idx {
+ return [self objectAtIndex:idx];
+}
+
+@end
+
+
+@implementation RACTupleUnpackingTrampoline
+
+#pragma mark Lifecycle
+
++ (instancetype)trampoline {
+ static dispatch_once_t onceToken;
+ static id trampoline = nil;
+ dispatch_once(&onceToken, ^{
+ trampoline = [[self alloc] init];
+ });
+
+ return trampoline;
+}
+
+- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables {
+ NSCParameterAssert(variables != nil);
+
+ [variables enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) {
+ __strong id *ptr = (__strong id *)value.pointerValue;
+ *ptr = tuple[index];
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.h
new file mode 100644
index 0000000..f6cd021
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.h
@@ -0,0 +1,18 @@
+//
+// RACTupleSequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class that adapts a RACTuple to the RACSequence interface.
+@interface RACTupleSequence : RACSequence
+
+// Returns a sequence for enumerating over the given backing array (from a
+// RACTuple), starting from the given offset.
++ (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.m
new file mode 100644
index 0000000..ba268f5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.m
@@ -0,0 +1,68 @@
+//
+// RACTupleSequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTupleSequence.h"
+#import "RACTuple.h"
+
+@interface RACTupleSequence ()
+
+// The array being sequenced, as taken from RACTuple.backingArray.
+@property (nonatomic, strong, readonly) NSArray *tupleBackingArray;
+
+// The index in the array from which the sequence starts.
+@property (nonatomic, assign, readonly) NSUInteger offset;
+
+@end
+
+@implementation RACTupleSequence
+
+#pragma mark Lifecycle
+
++ (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset {
+ NSCParameterAssert(offset <= backingArray.count);
+
+ if (offset == backingArray.count) return self.empty;
+
+ RACTupleSequence *seq = [[self alloc] init];
+ seq->_tupleBackingArray = backingArray;
+ seq->_offset = offset;
+ return seq;
+}
+
+#pragma mark RACSequence
+
+- (id)head {
+ id object = [self.tupleBackingArray objectAtIndex:self.offset];
+ return (object == RACTupleNil.tupleNil ? NSNull.null : object);
+}
+
+- (RACSequence *)tail {
+ RACSequence *sequence = [self.class sequenceWithTupleBackingArray:self.tupleBackingArray offset:self.offset + 1];
+ sequence.name = self.name;
+ return sequence;
+}
+
+- (NSArray *)array {
+ NSRange range = NSMakeRange(self.offset, self.tupleBackingArray.count - self.offset);
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:range.length];
+
+ [self.tupleBackingArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] options:0 usingBlock:^(id object, NSUInteger index, BOOL *stop) {
+ id mappedObject = (object == RACTupleNil.tupleNil ? NSNull.null : object);
+ [array addObject:mappedObject];
+ }];
+
+ return array;
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, tuple = %@ }", self.class, self, self.name, self.tupleBackingArray];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.h b/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.h
new file mode 100644
index 0000000..86a514b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.h
@@ -0,0 +1,14 @@
+//
+// RACUnarySequence.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequence.h"
+
+// Private class representing a sequence of exactly one value.
+@interface RACUnarySequence : RACSequence
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.m b/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.m
new file mode 100644
index 0000000..f72bf09
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.m
@@ -0,0 +1,81 @@
+//
+// RACUnarySequence.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-05-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACUnarySequence.h"
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACDescription.h"
+
+@interface RACUnarySequence ()
+
+// The single value stored in this sequence.
+@property (nonatomic, strong, readwrite) id head;
+
+@end
+
+@implementation RACUnarySequence
+
+#pragma mark Properties
+
+@synthesize head = _head;
+
+#pragma mark Lifecycle
+
++ (instancetype)return:(id)value {
+ RACUnarySequence *sequence = [[self alloc] init];
+ sequence.head = value;
+ return [sequence setNameWithFormat:@"+return: %@", [value rac_description]];
+}
+
+#pragma mark RACSequence
+
+- (RACSequence *)tail {
+ return nil;
+}
+
+- (instancetype)bind:(RACStreamBindBlock (^)(void))block {
+ RACStreamBindBlock bindBlock = block();
+ BOOL stop = NO;
+
+ RACSequence *result = (id)[bindBlock(self.head, &stop) setNameWithFormat:@"[%@] -bind:", self.name];
+ return result ?: self.class.empty;
+}
+
+#pragma mark NSCoding
+
+- (Class)classForCoder {
+ // Unary sequences should be encoded as themselves, not array sequences.
+ return self.class;
+}
+
+- (id)initWithCoder:(NSCoder *)coder {
+ id value = [coder decodeObjectForKey:@keypath(self.head)];
+ return [self.class return:value];
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ if (self.head != nil) [coder encodeObject:self.head forKey:@keypath(self.head)];
+}
+
+#pragma mark NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@ }", self.class, self, self.name, self.head];
+}
+
+- (NSUInteger)hash {
+ return [self.head hash];
+}
+
+- (BOOL)isEqual:(RACUnarySequence *)seq {
+ if (self == seq) return YES;
+ if (![seq isKindOfClass:RACUnarySequence.class]) return NO;
+
+ return self.head == seq.head || [(NSObject *)self.head isEqual:seq.head];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.h b/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.h
new file mode 100644
index 0000000..7713e1d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.h
@@ -0,0 +1,20 @@
+//
+// RACUnit.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/27/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+/// A unit represents an empty value.
+///
+/// It should never be necessary to create a unit yourself. Just use +defaultUnit.
+@interface RACUnit : NSObject
+
+/// A singleton instance.
++ (RACUnit *)defaultUnit;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.m b/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.m
new file mode 100644
index 0000000..76b8425
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACUnit.m
@@ -0,0 +1,27 @@
+//
+// RACUnit.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/27/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACUnit.h"
+
+
+@implementation RACUnit
+
+
+#pragma mark API
+
++ (RACUnit *)defaultUnit {
+ static dispatch_once_t onceToken;
+ static RACUnit *defaultUnit = nil;
+ dispatch_once(&onceToken, ^{
+ defaultUnit = [[self alloc] init];
+ });
+
+ return defaultUnit;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.h b/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.h
new file mode 100644
index 0000000..740b861
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.h
@@ -0,0 +1,16 @@
+//
+// RACValueTransformer.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/6/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// A private block based transformer.
+@interface RACValueTransformer : NSValueTransformer
+
++ (instancetype)transformerWithBlock:(id (^)(id value))block;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.m b/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.m
new file mode 100644
index 0000000..ccec07d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.m
@@ -0,0 +1,42 @@
+//
+// RACValueTransformer.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/6/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACValueTransformer.h"
+
+@interface RACValueTransformer ()
+@property (nonatomic, copy) id (^transformBlock)(id value);
+@end
+
+
+@implementation RACValueTransformer
+
+
+#pragma mark NSValueTransformer
+
++ (BOOL)allowsReverseTransformation {
+ return NO;
+}
+
+- (id)transformedValue:(id)value {
+ return self.transformBlock(value);
+}
+
+
+#pragma mark API
+
+@synthesize transformBlock;
+
++ (instancetype)transformerWithBlock:(id (^)(id value))block {
+ NSCParameterAssert(block != NULL);
+
+ RACValueTransformer *transformer = [[self alloc] init];
+ transformer.transformBlock = block;
+ return transformer;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Info.plist b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Info.plist
new file mode 100644
index 0000000..4e63eab
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.github.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2012 GitHub, Inc. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Prefix.pch b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Prefix.pch
new file mode 100644
index 0000000..aa9849d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Prefix.pch
@@ -0,0 +1,13 @@
+//
+// Prefix header for all source files of the 'ReactiveCocoa' target in the 'ReactiveCocoa' project
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+#endif
+
+#undef NSAssert
+#undef NSParameterAssert
+
+extern void NSAssert(int condition, ...) __attribute__((unavailable("Use NSCAssert instead.")));
+extern void NSParameterAssert(int condition, ...) __attribute__((unavailable("Use NSCParameterAssert instead.")));
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h
new file mode 100644
index 0000000..41f32d9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h
@@ -0,0 +1,74 @@
+//
+// ReactiveCocoa.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/5/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "EXTKeyPathCoding.h"
+#import "NSArray+RACSequenceAdditions.h"
+#import "NSData+RACSupport.h"
+#import "NSDictionary+RACSequenceAdditions.h"
+#import "NSEnumerator+RACSequenceAdditions.h"
+#import "NSFileHandle+RACSupport.h"
+#import "NSNotificationCenter+RACSupport.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACLifting.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "NSOrderedSet+RACSequenceAdditions.h"
+#import "NSSet+RACSequenceAdditions.h"
+#import "NSString+RACSequenceAdditions.h"
+#import "NSString+RACSupport.h"
+#import "NSIndexSet+RACSequenceAdditions.h"
+#import "NSURLConnection+RACSupport.h"
+#import "NSUserDefaults+RACSupport.h"
+#import "RACBehaviorSubject.h"
+#import "RACChannel.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACEvent.h"
+#import "RACGroupedSignal.h"
+#import "RACKVOChannel.h"
+#import "RACMulticastConnection.h"
+#import "RACQueueScheduler.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import "RACScopedDisposable.h"
+#import "RACSequence.h"
+#import "RACSerialDisposable.h"
+#import "RACSignal+Operations.h"
+#import "RACSignal.h"
+#import "RACStream.h"
+#import "RACSubject.h"
+#import "RACSubscriber.h"
+#import "RACSubscriptingAssignmentTrampoline.h"
+#import "RACTargetQueueScheduler.h"
+#import "RACTestScheduler.h"
+#import "RACTuple.h"
+#import "RACUnit.h"
+
+#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
+ #import "UIActionSheet+RACSignalSupport.h"
+ #import "UIAlertView+RACSignalSupport.h"
+ #import "UIBarButtonItem+RACCommandSupport.h"
+ #import "UIButton+RACCommandSupport.h"
+ #import "UICollectionReusableView+RACSignalSupport.h"
+ #import "UIControl+RACSignalSupport.h"
+ #import "UIDatePicker+RACSignalSupport.h"
+ #import "UIGestureRecognizer+RACSignalSupport.h"
+ #import "UISegmentedControl+RACSignalSupport.h"
+ #import "UISlider+RACSignalSupport.h"
+ #import "UIStepper+RACSignalSupport.h"
+ #import "UISwitch+RACSignalSupport.h"
+ #import "UITableViewCell+RACSignalSupport.h"
+ #import "UITextField+RACSignalSupport.h"
+ #import "UITextView+RACSignalSupport.h"
+#elif TARGET_OS_MAC
+ #import "NSControl+RACCommandSupport.h"
+ #import "NSControl+RACTextSignalSupport.h"
+ #import "NSObject+RACAppKitBindings.h"
+ #import "NSText+RACSignalSupport.h"
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.h
new file mode 100644
index 0000000..3d667e9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.h
@@ -0,0 +1,32 @@
+//
+// UIActionSheet+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACDelegateProxy;
+@class RACSignal;
+
+@interface UIActionSheet (RACSignalSupport)
+
+/// A delegate proxy which will be set as the receiver's delegate when any of the
+/// methods in this category are used.
+@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
+
+/// Creates a signal for button clicks on the receiver.
+///
+/// When this method is invoked, the `rac_delegateProxy` will become the
+/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy
+/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't
+/// know how to handle. Setting the receiver's `delegate` afterward is
+/// considered undefined behavior.
+///
+/// Returns a signal which will send the index of the specific button clicked.
+/// The signal will complete when the receiver is deallocated.
+- (RACSignal *)rac_buttonClickedSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.m
new file mode 100644
index 0000000..6198797
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIActionSheet+RACSignalSupport.m
@@ -0,0 +1,50 @@
+//
+// UIActionSheet+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIActionSheet+RACSignalSupport.h"
+#import "RACDelegateProxy.h"
+#import "RACSignal+Operations.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACTuple.h"
+#import "NSObject+RACDescription.h"
+#import <objc/runtime.h>
+
+@implementation UIActionSheet (RACSignalSupport)
+
+static void RACUseDelegateProxy(UIActionSheet *self) {
+ if (self.delegate == self.rac_delegateProxy) return;
+
+ self.rac_delegateProxy.rac_proxiedDelegate = self.delegate;
+ self.delegate = (id)self.rac_delegateProxy;
+}
+
+- (RACDelegateProxy *)rac_delegateProxy {
+ RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
+ if (proxy == nil) {
+ proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIActionSheetDelegate)];
+ objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }
+
+ return proxy;
+}
+
+- (RACSignal *)rac_buttonClickedSignal {
+ RACSignal *signal = [[[[self.rac_delegateProxy
+ signalForSelector:@selector(actionSheet:clickedButtonAtIndex:)]
+ reduceEach:^(UIActionSheet *actionSheet, NSNumber *buttonIndex) {
+ return buttonIndex;
+ }]
+ takeUntil:self.rac_willDeallocSignal]
+ setNameWithFormat:@"%@ -rac_buttonClickedSignal", [self rac_description]];
+
+ RACUseDelegateProxy(self);
+
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.h
new file mode 100644
index 0000000..4586460
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.h
@@ -0,0 +1,32 @@
+//
+// UIAlertView+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Henrik Hodne on 6/16/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACDelegateProxy;
+@class RACSignal;
+
+@interface UIAlertView (RACSignalSupport)
+
+/// A delegate proxy which will be set as the receiver's delegate when any of the
+/// methods in this category are used.
+@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
+
+/// Creates a signal for button clicks on the receiver.
+///
+/// When this method is invoked, the `rac_delegateProxy` will become the
+/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy
+/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't
+/// know how to handle. Setting the receiver's `delegate` afterward is considered
+/// undefined behavior.
+///
+/// Returns a signal which will send the index of the specific button clicked.
+/// The signal will complete itself when the receiver is deallocated.
+- (RACSignal *)rac_buttonClickedSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.m
new file mode 100644
index 0000000..bf15b27
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIAlertView+RACSignalSupport.m
@@ -0,0 +1,50 @@
+//
+// UIAlertView+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Henrik Hodne on 6/16/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIAlertView+RACSignalSupport.h"
+#import "RACDelegateProxy.h"
+#import "RACSignal+Operations.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "RACTuple.h"
+#import <objc/runtime.h>
+
+@implementation UIAlertView (RACSignalSupport)
+
+static void RACUseDelegateProxy(UIAlertView *self) {
+ if (self.delegate == self.rac_delegateProxy) return;
+
+ self.rac_delegateProxy.rac_proxiedDelegate = self.delegate;
+ self.delegate = (id)self.rac_delegateProxy;
+}
+
+- (RACDelegateProxy *)rac_delegateProxy {
+ RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
+ if (proxy == nil) {
+ proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIAlertViewDelegate)];
+ objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }
+
+ return proxy;
+}
+
+- (RACSignal *)rac_buttonClickedSignal {
+ RACSignal *signal = [[[[self.rac_delegateProxy
+ signalForSelector:@selector(alertView:clickedButtonAtIndex:)]
+ reduceEach:^(UIAlertView *alertView, NSNumber *buttonIndex) {
+ return buttonIndex;
+ }]
+ takeUntil:self.rac_willDeallocSignal]
+ setNameWithFormat:@"%@ -rac_buttonClickedSignal", [self rac_description]];
+
+ RACUseDelegateProxy(self);
+
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.h
new file mode 100644
index 0000000..c04648a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.h
@@ -0,0 +1,22 @@
+//
+// UIBarButtonItem+RACCommandSupport.h
+// ReactiveCocoa
+//
+// Created by Kyle LeNeau on 3/27/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACCommand;
+
+@interface UIBarButtonItem (RACCommandSupport)
+
+/// Sets the control's command. When the control is clicked, the command is
+/// executed with the sender of the event. The control's enabledness is bound
+/// to the command's `canExecute`.
+///
+/// Note: this will reset the control's target and action.
+@property (nonatomic, strong) RACCommand *rac_command;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.m
new file mode 100644
index 0000000..91b7d19
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.m
@@ -0,0 +1,55 @@
+//
+// UIBarButtonItem+RACCommandSupport.m
+// ReactiveCocoa
+//
+// Created by Kyle LeNeau on 3/27/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIBarButtonItem+RACCommandSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCommand.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+#import <objc/runtime.h>
+
+static void *UIControlRACCommandKey = &UIControlRACCommandKey;
+static void *UIControlEnabledDisposableKey = &UIControlEnabledDisposableKey;
+
+@implementation UIBarButtonItem (RACCommandSupport)
+
+- (RACCommand *)rac_command {
+ return objc_getAssociatedObject(self, UIControlRACCommandKey);
+}
+
+- (void)setRac_command:(RACCommand *)command {
+ objc_setAssociatedObject(self, UIControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Check for stored signal in order to remove it and add a new one
+ RACDisposable *disposable = objc_getAssociatedObject(self, UIControlEnabledDisposableKey);
+ [disposable dispose];
+
+ if (command == nil) return;
+
+ disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self];
+ objc_setAssociatedObject(self, UIControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ [self rac_hijackActionAndTargetIfNeeded];
+}
+
+- (void)rac_hijackActionAndTargetIfNeeded {
+ SEL hijackSelector = @selector(rac_commandPerformAction:);
+ if (self.target == self && self.action == hijackSelector) return;
+
+ if (self.target != nil) NSLog(@"WARNING: UIBarButtonItem.rac_command hijacks the control's existing target and action.");
+
+ self.target = self;
+ self.action = hijackSelector;
+}
+
+- (void)rac_commandPerformAction:(id)sender {
+ [self.rac_command execute:sender];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.h
new file mode 100644
index 0000000..642ba8e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.h
@@ -0,0 +1,20 @@
+//
+// UIButton+RACCommandSupport.h
+// ReactiveCocoa
+//
+// Created by Ash Furrow on 2013-06-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACCommand;
+
+@interface UIButton (RACCommandSupport)
+
+/// Sets the button's command. When the button is clicked, the command is
+/// executed with the sender of the event. The button's enabledness is bound
+/// to the command's `canExecute`.
+@property (nonatomic, strong) RACCommand *rac_command;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.m
new file mode 100644
index 0000000..2f5f1a0
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIButton+RACCommandSupport.m
@@ -0,0 +1,57 @@
+//
+// UIButton+RACCommandSupport.m
+// ReactiveCocoa
+//
+// Created by Ash Furrow on 2013-06-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIButton+RACCommandSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCommand.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+#import <objc/runtime.h>
+
+static void *UIButtonRACCommandKey = &UIButtonRACCommandKey;
+static void *UIButtonEnabledDisposableKey = &UIButtonEnabledDisposableKey;
+
+@implementation UIButton (RACCommandSupport)
+
+- (RACCommand *)rac_command {
+ return objc_getAssociatedObject(self, UIButtonRACCommandKey);
+}
+
+- (void)setRac_command:(RACCommand *)command {
+ objc_setAssociatedObject(self, UIButtonRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Check for stored signal in order to remove it and add a new one
+ RACDisposable *disposable = objc_getAssociatedObject(self, UIButtonEnabledDisposableKey);
+ [disposable dispose];
+
+ if (command == nil) return;
+
+ disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self];
+ objc_setAssociatedObject(self, UIButtonEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ [self rac_hijackActionAndTargetIfNeeded];
+}
+
+- (void)rac_hijackActionAndTargetIfNeeded {
+ SEL hijackSelector = @selector(rac_commandPerformAction:);
+
+ for (NSString *selector in [self actionsForTarget:self forControlEvent:UIControlEventTouchUpInside]) {
+ if (hijackSelector == NSSelectorFromString(selector)) {
+ return;
+ }
+ }
+
+ [self addTarget:self action:hijackSelector forControlEvents:UIControlEventTouchUpInside];
+}
+
+- (void)rac_commandPerformAction:(id)sender {
+ [self.rac_command execute:sender];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.h
new file mode 100644
index 0000000..96b3dfe
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.h
@@ -0,0 +1,29 @@
+//
+// UICollectionReusableView+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Kent Wong on 2013-10-04.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal;
+
+// This category is only applicable to iOS >= 6.0.
+@interface UICollectionReusableView (RACSignalSupport)
+
+/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon
+/// the receiver.
+///
+/// Examples
+///
+/// [[[self.cancelButton
+/// rac_signalForControlEvents:UIControlEventTouchUpInside]
+/// takeUntil:self.rac_prepareForReuseSignal]
+/// subscribeNext:^(UIButton *x) {
+/// // do other things
+/// }];
+@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.m
new file mode 100644
index 0000000..7e8680c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UICollectionReusableView+RACSignalSupport.m
@@ -0,0 +1,31 @@
+//
+// UICollectionReusableView+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Kent Wong on 2013-10-04.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UICollectionReusableView+RACSignalSupport.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACUnit.h"
+#import <objc/runtime.h>
+
+@implementation UICollectionReusableView (RACSignalSupport)
+
+- (RACSignal *)rac_prepareForReuseSignal {
+ RACSignal *signal = objc_getAssociatedObject(self, _cmd);
+ if (signal != nil) return signal;
+
+ signal = [[[self
+ rac_signalForSelector:@selector(prepareForReuse)]
+ mapReplace:RACUnit.defaultUnit]
+ setNameWithFormat:@"%@ -rac_prepareForReuseSignal", self.rac_description];
+
+ objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.h
new file mode 100644
index 0000000..2de86cf
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.h
@@ -0,0 +1,19 @@
+//
+// UIControl+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal;
+
+@interface UIControl (RACSignalSupport)
+
+/// Creates and returns a signal that sends the sender of the control event
+/// whenever one of the control events is triggered.
+- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.m
new file mode 100644
index 0000000..9e818ca
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.m
@@ -0,0 +1,41 @@
+//
+// UIControl+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "UIControl+RACSignalSupport.h"
+#import "EXTScope.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACSubscriber+Private.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+
+@implementation UIControl (RACSignalSupport)
+
+- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
+ @weakify(self);
+
+ return [[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ @strongify(self);
+
+ [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
+ [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ [subscriber sendCompleted];
+ }]];
+
+ return [RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
+ }];
+ }]
+ setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", [self rac_description], (unsigned long)controlEvents];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.h b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.h
new file mode 100644
index 0000000..7f778a4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.h
@@ -0,0 +1,29 @@
+//
+// UIControl+RACSignalSupportPrivate.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 06/08/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UIControl (RACSignalSupportPrivate)
+
+// Adds a RACChannel-based interface to the receiver for the given
+// UIControlEvents and exposes it.
+//
+// controlEvents - A mask of UIControlEvents on which to send new values.
+// key - The key whose value should be read and set when a control
+// event fires and when a value is sent to the
+// RACChannelTerminal respectively.
+// nilValue - The value to be assigned to the key when `nil` is sent to the
+// RACChannelTerminal.
+//
+// Returns a RACChannelTerminal which will send future values from the receiver,
+// and update the receiver when values are sent to the terminal.
+- (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(id)nilValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.m b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.m
new file mode 100644
index 0000000..b73f817
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupportPrivate.m
@@ -0,0 +1,50 @@
+//
+// UIControl+RACSignalSupportPrivate.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 06/08/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIControl+RACSignalSupportPrivate.h"
+#import "EXTScope.h"
+#import "NSInvocation+RACTypeParsing.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACLifting.h"
+#import "RACChannel.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+#import "UIControl+RACSignalSupport.h"
+
+@implementation UIControl (RACSignalSupportPrivate)
+
+- (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(id)nilValue {
+ NSCParameterAssert(key.length > 0);
+ key = [key copy];
+ RACChannel *channel = [[RACChannel alloc] init];
+
+ [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ [channel.followingTerminal sendCompleted];
+ }]];
+
+ RACSignal *eventSignal = [[[self
+ rac_signalForControlEvents:controlEvents]
+ mapReplace:key]
+ takeUntil:[[channel.followingTerminal
+ ignoreValues]
+ catchTo:RACSignal.empty]];
+ [[self
+ rac_liftSelector:@selector(valueForKey:) withSignals:eventSignal, nil]
+ subscribe:channel.followingTerminal];
+
+ RACSignal *valuesSignal = [channel.followingTerminal
+ map:^(id value) {
+ return value ?: nilValue;
+ }];
+ [self rac_liftSelector:@selector(setValue:forKey:) withSignals:valuesSignal, [RACSignal return:key], nil];
+
+ return channel.leadingTerminal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.h
new file mode 100644
index 0000000..e620dfc
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.h
@@ -0,0 +1,24 @@
+//
+// UIDatePicker+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UIDatePicker (RACSignalSupport)
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// nilValue - The date to set when the terminal receives `nil`.
+///
+/// Returns a RACChannelTerminal that sends the receiver's date whenever the
+/// UIControlEventValueChanged control event is fired, and sets the date to the
+/// values it receives.
+- (RACChannelTerminal *)rac_newDateChannelWithNilValue:(NSDate *)nilValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.m
new file mode 100644
index 0000000..fbae59c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIDatePicker+RACSignalSupport.m
@@ -0,0 +1,20 @@
+//
+// UIDatePicker+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIDatePicker+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UIDatePicker (RACSignalSupport)
+
+- (RACChannelTerminal *)rac_newDateChannelWithNilValue:(NSDate *)nilValue {
+ return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.date) nilValue:nilValue];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.h
new file mode 100644
index 0000000..ee774fe
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.h
@@ -0,0 +1,18 @@
+//
+// UIGestureRecognizer+RACSignalSupport.h
+// Talks
+//
+// Created by Josh Vera on 5/5/13.
+// Copyright (c) 2013 GitHub. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal;
+
+@interface UIGestureRecognizer (RACSignalSupport)
+
+/// Returns a signal that sends the receiver when its gesture occurs.
+- (RACSignal *)rac_gestureSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.m
new file mode 100644
index 0000000..afd5127
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.m
@@ -0,0 +1,40 @@
+//
+// UIGestureRecognizer+RACSignalSupport.m
+// Talks
+//
+// Created by Josh Vera on 5/5/13.
+// Copyright (c) 2013 GitHub. All rights reserved.
+//
+
+#import "UIGestureRecognizer+RACSignalSupport.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "RACSubscriber.h"
+#import "NSObject+RACDescription.h"
+
+@implementation UIGestureRecognizer (RACSignalSupport)
+
+- (RACSignal *)rac_gestureSignal {
+ @weakify(self);
+
+ return [[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ @strongify(self);
+
+ [self addTarget:subscriber action:@selector(sendNext:)];
+ [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ [subscriber sendCompleted];
+ }]];
+
+ return [RACDisposable disposableWithBlock:^{
+ @strongify(self);
+ [self removeTarget:subscriber action:@selector(sendNext:)];
+ }];
+ }]
+ setNameWithFormat:@"%@ -rac_gestureSignal", [self rac_description]];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.h
new file mode 100644
index 0000000..0bd1bf1
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.h
@@ -0,0 +1,22 @@
+//
+// UIRefreshControl+RACCommandSupport.h
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-10-17.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACCommand;
+
+@interface UIRefreshControl (RACCommandSupport)
+
+/// Manipulate the RACCommand property associated with this refresh control.
+///
+/// When this refresh control is activated by the user, the command will be
+/// executed. Upon completion or error of the execution signal, -endRefreshing
+/// will be invoked.
+@property (nonatomic, strong) RACCommand *rac_command;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.m
new file mode 100644
index 0000000..9447f3e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl+RACCommandSupport.m
@@ -0,0 +1,59 @@
+//
+// UIRefreshControl+RACCommandSupport.m
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-10-17.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIRefreshControl+RACCommandSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACDisposable.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "UIControl+RACSignalSupport.h"
+#import <objc/runtime.h>
+
+static void *UIRefreshControlRACCommandKey = &UIRefreshControlRACCommandKey;
+static void *UIRefreshControlDisposableKey = &UIRefreshControlDisposableKey;
+
+@implementation UIRefreshControl (RACCommandSupport)
+
+- (RACCommand *)rac_command {
+ return objc_getAssociatedObject(self, UIRefreshControlRACCommandKey);
+}
+
+- (void)setRac_command:(RACCommand *)command {
+ objc_setAssociatedObject(self, UIRefreshControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Dispose of any active command associations.
+ [objc_getAssociatedObject(self, UIRefreshControlDisposableKey) dispose];
+
+ if (command == nil) return;
+
+ // Like RAC(self, enabled) = command.enabled; but with access to disposable.
+ RACDisposable *enabledDisposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self];
+
+ RACDisposable *executionDisposable = [[[[self
+ rac_signalForControlEvents:UIControlEventValueChanged]
+ map:^(UIRefreshControl *x) {
+ return [[[command
+ execute:x]
+ catchTo:[RACSignal empty]]
+ then:^{
+ return [RACSignal return:x];
+ }];
+ }]
+ concat]
+ subscribeNext:^(UIRefreshControl *x) {
+ [x endRefreshing];
+ }];
+
+ RACDisposable *commandDisposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ enabledDisposable, executionDisposable ]];
+ objc_setAssociatedObject(self, UIRefreshControlDisposableKey, commandDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.h
new file mode 100644
index 0000000..2d3c3e7
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.h
@@ -0,0 +1,24 @@
+//
+// UISegmentedControl+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UISegmentedControl (RACSignalSupport)
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// nilValue - The segment to select when the terminal receives `nil`.
+///
+/// Returns a RACChannelTerminal that sends the receiver's currently selected
+/// segment's index whenever the UIControlEventValueChanged control event is
+/// fired, and sets the selected segment index to the values it receives.
+- (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(NSNumber *)nilValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.m
new file mode 100644
index 0000000..9ccea2b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISegmentedControl+RACSignalSupport.m
@@ -0,0 +1,20 @@
+//
+// UISegmentedControl+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UISegmentedControl+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UISegmentedControl (RACSignalSupport)
+
+- (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(NSNumber *)nilValue {
+ return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.selectedSegmentIndex) nilValue:nilValue];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.h
new file mode 100644
index 0000000..75626ad
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.h
@@ -0,0 +1,24 @@
+//
+// UISlider+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UISlider (RACSignalSupport)
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// nilValue - The value to set when the terminal receives `nil`.
+///
+/// Returns a RACChannelTerminal that sends the receiver's value whenever the
+/// UIControlEventValueChanged control event is fired, and sets the value to the
+/// values it receives.
+- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.m
new file mode 100644
index 0000000..2d37a3b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISlider+RACSignalSupport.m
@@ -0,0 +1,20 @@
+//
+// UISlider+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UISlider+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UISlider (RACSignalSupport)
+
+- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue {
+ return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.h
new file mode 100644
index 0000000..da6d97b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.h
@@ -0,0 +1,24 @@
+//
+// UIStepper+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UIStepper (RACSignalSupport)
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// nilValue - The value to set when the terminal receives `nil`.
+///
+/// Returns a RACChannelTerminal that sends the receiver's value whenever the
+/// UIControlEventValueChanged control event is fired, and sets the value to the
+/// values it receives.
+- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.m
new file mode 100644
index 0000000..d8c5e81
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UIStepper+RACSignalSupport.m
@@ -0,0 +1,20 @@
+//
+// UIStepper+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIStepper+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UIStepper (RACSignalSupport)
+
+- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue {
+ return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.h
new file mode 100644
index 0000000..1313a2c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.h
@@ -0,0 +1,22 @@
+//
+// UISwitch+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACChannelTerminal;
+
+@interface UISwitch (RACSignalSupport)
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// Returns a RACChannelTerminal that sends whether the receiver is on whenever
+/// the UIControlEventValueChanged control event is fired, and sets it on or off
+/// when it receives @YES or @NO respectively.
+- (RACChannelTerminal *)rac_newOnChannel;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.m
new file mode 100644
index 0000000..1733fc5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UISwitch+RACSignalSupport.m
@@ -0,0 +1,20 @@
+//
+// UISwitch+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 20/07/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UISwitch+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UISwitch (RACSignalSupport)
+
+- (RACChannelTerminal *)rac_newOnChannel {
+ return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.on) nilValue:@NO];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.h
new file mode 100644
index 0000000..c29d47c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.h
@@ -0,0 +1,28 @@
+//
+// UITableViewCell+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal;
+
+@interface UITableViewCell (RACSignalSupport)
+
+/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon
+/// the receiver.
+///
+/// Examples
+///
+/// [[[self.cancelButton
+/// rac_signalForControlEvents:UIControlEventTouchUpInside]
+/// takeUntil:self.rac_prepareForReuseSignal]
+/// subscribeNext:^(UIButton *x) {
+/// // do other things
+/// }];
+@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.m
new file mode 100644
index 0000000..8a81c30
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewCell+RACSignalSupport.m
@@ -0,0 +1,31 @@
+//
+// UITableViewCell+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UITableViewCell+RACSignalSupport.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACUnit.h"
+#import <objc/runtime.h>
+
+@implementation UITableViewCell (RACSignalSupport)
+
+- (RACSignal *)rac_prepareForReuseSignal {
+ RACSignal *signal = objc_getAssociatedObject(self, _cmd);
+ if (signal != nil) return signal;
+
+ signal = [[[self
+ rac_signalForSelector:@selector(prepareForReuse)]
+ mapReplace:RACUnit.defaultUnit]
+ setNameWithFormat:@"%@ -rac_prepareForReuseSignal", self.rac_description];
+
+ objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.h
new file mode 100644
index 0000000..6c5d6b8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.h
@@ -0,0 +1,29 @@
+//
+// UITableViewHeaderFooterView+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Syo Ikeda on 12/30/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal;
+
+// This category is only applicable to iOS >= 6.0.
+@interface UITableViewHeaderFooterView (RACSignalSupport)
+
+/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon
+/// the receiver.
+///
+/// Examples
+///
+/// [[[self.cancelButton
+/// rac_signalForControlEvents:UIControlEventTouchUpInside]
+/// takeUntil:self.rac_prepareForReuseSignal]
+/// subscribeNext:^(UIButton *x) {
+/// // do other things
+/// }];
+@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.m
new file mode 100644
index 0000000..0ecfc37
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITableViewHeaderFooterView+RACSignalSupport.m
@@ -0,0 +1,31 @@
+//
+// UITableViewHeaderFooterView+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Syo Ikeda on 12/30/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UITableViewHeaderFooterView+RACSignalSupport.h"
+#import "NSObject+RACDescription.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACUnit.h"
+#import <objc/runtime.h>
+
+@implementation UITableViewHeaderFooterView (RACSignalSupport)
+
+- (RACSignal *)rac_prepareForReuseSignal {
+ RACSignal *signal = objc_getAssociatedObject(self, _cmd);
+ if (signal != nil) return signal;
+
+ signal = [[[self
+ rac_signalForSelector:@selector(prepareForReuse)]
+ mapReplace:RACUnit.defaultUnit]
+ setNameWithFormat:@"%@ -rac_prepareForReuseSignal", self.rac_description];
+
+ objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.h
new file mode 100644
index 0000000..4ff6ee4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.h
@@ -0,0 +1,27 @@
+//
+// UITextField+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACSignal, RACChannelTerminal;
+
+@interface UITextField (RACSignalSupport)
+
+/// Creates and returns a signal for the text of the field. It always starts with
+/// the current text. The signal sends next when the UIControlEventEditingChanged
+/// or UIControlEventEditingDidBegin control event is fired on the control.
+- (RACSignal *)rac_textSignal;
+
+/// Creates a new RACChannel-based binding to the receiver.
+///
+/// Returns a RACChannelTerminal that sends the receiver's text whenever the
+/// UIControlEventEditingChanged or UIControlEventEditingDidBegin control event
+/// is fired, and sets the text to the values it receives.
+- (RACChannelTerminal *)rac_newTextChannel;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.m
new file mode 100644
index 0000000..ca2c68c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.m
@@ -0,0 +1,39 @@
+//
+// UITextField+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 4/17/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "UITextField+RACSignalSupport.h"
+#import "EXTKeyPathCoding.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACDescription.h"
+#import "RACSignal+Operations.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UIControl+RACSignalSupportPrivate.h"
+
+@implementation UITextField (RACSignalSupport)
+
+- (RACSignal *)rac_textSignal {
+ @weakify(self);
+ return [[[[[RACSignal
+ defer:^{
+ @strongify(self);
+ return [RACSignal return:self];
+ }]
+ concat:[self rac_signalForControlEvents:UIControlEventEditingChanged | UIControlEventEditingDidBegin]]
+ map:^(UITextField *x) {
+ return x.text;
+ }]
+ takeUntil:self.rac_willDeallocSignal]
+ setNameWithFormat:@"%@ -rac_textSignal", [self rac_description]];
+}
+
+- (RACChannelTerminal *)rac_newTextChannel {
+ return [self rac_channelForControlEvents:UIControlEventEditingChanged | UIControlEventEditingDidBegin key:@keypath(self.text) nilValue:@""];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.h
new file mode 100644
index 0000000..174b1ba
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.h
@@ -0,0 +1,39 @@
+//
+// UITextView+RACSignalSupport.h
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/18/12.
+// Copyright (c) 2012 Cody Krieger. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class RACDelegateProxy;
+@class RACSignal;
+
+@interface UITextView (RACSignalSupport)
+
+/// A delegate proxy which will be set as the receiver's delegate when any of the
+/// methods in this category are used.
+@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
+
+/// Creates a signal for the text of the receiver.
+///
+/// When this method is invoked, the `rac_delegateProxy` will become the
+/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy
+/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't
+/// know how to handle. Setting the receiver's `delegate` afterward is
+/// considered undefined behavior.
+///
+/// Returns a signal which will send the current text upon subscription, then
+/// again whenever the receiver's text is changed. The signal will complete when
+/// the receiver is deallocated.
+- (RACSignal *)rac_textSignal;
+
+@end
+
+@interface UITextView (RACSignalSupportUnavailable)
+
+- (RACSignal *)rac_signalForDelegateMethod:(SEL)method __attribute__((unavailable("Use -rac_signalForSelector:fromProtocol: instead")));
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.m
new file mode 100644
index 0000000..aa1cf12
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.m
@@ -0,0 +1,56 @@
+//
+// UITextView+RACSignalSupport.m
+// ReactiveCocoa
+//
+// Created by Cody Krieger on 5/18/12.
+// Copyright (c) 2012 Cody Krieger. All rights reserved.
+//
+
+#import "UITextView+RACSignalSupport.h"
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACDelegateProxy.h"
+#import "RACSignal+Operations.h"
+#import "RACTuple.h"
+#import "NSObject+RACDescription.h"
+#import <objc/runtime.h>
+
+@implementation UITextView (RACSignalSupport)
+
+static void RACUseDelegateProxy(UITextView *self) {
+ if (self.delegate == self.rac_delegateProxy) return;
+
+ self.rac_delegateProxy.rac_proxiedDelegate = self.delegate;
+ self.delegate = (id)self.rac_delegateProxy;
+}
+
+- (RACDelegateProxy *)rac_delegateProxy {
+ RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
+ if (proxy == nil) {
+ proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)];
+ objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }
+
+ return proxy;
+}
+
+- (RACSignal *)rac_textSignal {
+ @weakify(self);
+ RACSignal *signal = [[[[[RACSignal
+ defer:^{
+ @strongify(self);
+ return [RACSignal return:RACTuplePack(self)];
+ }]
+ concat:[self.rac_delegateProxy signalForSelector:@selector(textViewDidChange:)]]
+ reduceEach:^(UITextView *x) {
+ return x.text;
+ }]
+ takeUntil:self.rac_willDeallocSignal]
+ setNameWithFormat:@"%@ -rac_textSignal", [self rac_description]];
+
+ RACUseDelegateProxy(self);
+
+ return signal;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/en.lproj/InfoPlist.strings b/ReactiveCocoaFramework/ReactiveCocoa/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTKeyPathCoding.h b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTKeyPathCoding.h
new file mode 100644
index 0000000..f34dc4a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTKeyPathCoding.h
@@ -0,0 +1,68 @@
+//
+// EXTKeyPathCoding.h
+// extobjc
+//
+// Created by Justin Spahr-Summers on 19.06.12.
+// Copyright (C) 2012 Justin Spahr-Summers.
+// Released under the MIT license.
+//
+
+#import <Foundation/Foundation.h>
+#import "metamacros.h"
+
+/**
+ * \@keypath allows compile-time verification of key paths. Given a real object
+ * receiver and key path:
+ *
+ * @code
+
+NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String);
+// => @"lowercaseString.UTF8String"
+
+NSString *versionPath = @keypath(NSObject, version);
+// => @"version"
+
+NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString);
+// => @"lowercaseString"
+
+ * @endcode
+ *
+ * ... the macro returns an \c NSString containing all but the first path
+ * component or argument (e.g., @"lowercaseString.UTF8String", @"version").
+ *
+ * In addition to simply creating a key path, this macro ensures that the key
+ * path is valid at compile-time (causing a syntax error if not), and supports
+ * refactoring, such that changing the name of the property will also update any
+ * uses of \@keypath.
+ */
+#define keypath(...) \
+ metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))
+
+#define keypath1(PATH) \
+ (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))
+
+#define keypath2(OBJ, PATH) \
+ (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
+
+/**
+ * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object
+ * receiver, collection object receiver and related keypaths:
+ *
+ * @code
+
+ NSString *employessFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName)
+ // => @"employees.firstName"
+
+ NSString *employessFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName)
+ // => @"employees.firstName"
+
+ * @endcode
+ *
+ */
+#define collectionKeypath(...) \
+ metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__))
+
+#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String])
+
+#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(OBJ, PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String])
+
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.h b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.h
new file mode 100644
index 0000000..ab4e11d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.h
@@ -0,0 +1,122 @@
+//
+// EXTRuntimeExtensions.h
+// extobjc
+//
+// Created by Justin Spahr-Summers on 2011-03-05.
+// Copyright (C) 2012 Justin Spahr-Summers.
+// Released under the MIT license.
+//
+
+#import <objc/runtime.h>
+
+/**
+ * Describes the memory management policy of a property.
+ */
+typedef enum {
+ /**
+ * The value is assigned.
+ */
+ rac_propertyMemoryManagementPolicyAssign = 0,
+
+ /**
+ * The value is retained.
+ */
+ rac_propertyMemoryManagementPolicyRetain,
+
+ /**
+ * The value is copied.
+ */
+ rac_propertyMemoryManagementPolicyCopy
+} rac_propertyMemoryManagementPolicy;
+
+/**
+ * Describes the attributes and type information of a property.
+ */
+typedef struct {
+ /**
+ * Whether this property was declared with the \c readonly attribute.
+ */
+ BOOL readonly;
+
+ /**
+ * Whether this property was declared with the \c nonatomic attribute.
+ */
+ BOOL nonatomic;
+
+ /**
+ * Whether the property is a weak reference.
+ */
+ BOOL weak;
+
+ /**
+ * Whether the property is eligible for garbage collection.
+ */
+ BOOL canBeCollected;
+
+ /**
+ * Whether this property is defined with \c \@dynamic.
+ */
+ BOOL dynamic;
+
+ /**
+ * The memory management policy for this property. This will always be
+ * #rac_propertyMemoryManagementPolicyAssign if #readonly is \c YES.
+ */
+ rac_propertyMemoryManagementPolicy memoryManagementPolicy;
+
+ /**
+ * The selector for the getter of this property. This will reflect any
+ * custom \c getter= attribute provided in the property declaration, or the
+ * inferred getter name otherwise.
+ */
+ SEL getter;
+
+ /**
+ * The selector for the setter of this property. This will reflect any
+ * custom \c setter= attribute provided in the property declaration, or the
+ * inferred setter name otherwise.
+ *
+ * @note If #readonly is \c YES, this value will represent what the setter
+ * \e would be, if the property were writable.
+ */
+ SEL setter;
+
+ /**
+ * The backing instance variable for this property, or \c NULL if \c
+ * \c @synthesize was not used, and therefore no instance variable exists. This
+ * would also be the case if the property is implemented dynamically.
+ */
+ const char *ivar;
+
+ /**
+ * If this property is defined as being an instance of a specific class,
+ * this will be the class object representing it.
+ *
+ * This will be \c nil if the property was defined as type \c id, if the
+ * property is not of an object type, or if the class could not be found at
+ * runtime.
+ */
+ Class objectClass;
+
+ /**
+ * The type encoding for the value of this property. This is the type as it
+ * would be returned by the \c \@encode() directive.
+ */
+ char type[];
+} rac_propertyAttributes;
+
+/**
+ * Finds the instance method named \a aSelector on \a aClass and returns it, or
+ * returns \c NULL if no such instance method exists. Unlike \c
+ * class_getInstanceMethod(), this does not search superclasses.
+ *
+ * @note To get class methods in this manner, use a metaclass for \a aClass.
+ */
+Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector);
+
+/**
+ * Returns a pointer to a structure containing information about \a property.
+ * You must \c free() the returned pointer. Returns \c NULL if there is an error
+ * obtaining information from \a property.
+ */
+rac_propertyAttributes *rac_copyPropertyAttributes (objc_property_t property);
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.m b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.m
new file mode 100644
index 0000000..4d35a3e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTRuntimeExtensions.m
@@ -0,0 +1,232 @@
+//
+// EXTRuntimeExtensions.m
+// extobjc
+//
+// Created by Justin Spahr-Summers on 2011-03-05.
+// Copyright (C) 2012 Justin Spahr-Summers.
+// Released under the MIT license.
+//
+
+#import "EXTRuntimeExtensions.h"
+#import <ctype.h>
+#import <libkern/OSAtomic.h>
+#import <objc/message.h>
+#import <pthread.h>
+#import <stdio.h>
+#import <stdlib.h>
+#import <string.h>
+
+rac_propertyAttributes *rac_copyPropertyAttributes (objc_property_t property) {
+ const char * const attrString = property_getAttributes(property);
+ if (!attrString) {
+ fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property));
+ return NULL;
+ }
+
+ if (attrString[0] != 'T') {
+ fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property));
+ return NULL;
+ }
+
+ const char *typeString = attrString + 1;
+ const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
+ if (!next) {
+ fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+ return NULL;
+ }
+
+ size_t typeLength = (size_t)(next - typeString);
+ if (!typeLength) {
+ fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+ return NULL;
+ }
+
+ // allocate enough space for the structure and the type string (plus a NUL)
+ rac_propertyAttributes *attributes = calloc(1, sizeof(rac_propertyAttributes) + typeLength + 1);
+ if (!attributes) {
+ fprintf(stderr, "ERROR: Could not allocate rac_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+ return NULL;
+ }
+
+ // copy the type string
+ strncpy(attributes->type, typeString, typeLength);
+ attributes->type[typeLength] = '\0';
+
+ // if this is an object type, and immediately followed by a quoted string...
+ if (typeString[0] == *(@encode(id)) && typeString[1] == '"') {
+ // we should be able to extract a class name
+ const char *className = typeString + 2;
+ next = strchr(className, '"');
+
+ if (!next) {
+ fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+ return NULL;
+ }
+
+ if (className != next) {
+ size_t classNameLength = (size_t)(next - className);
+ char trimmedName[classNameLength + 1];
+
+ strncpy(trimmedName, className, classNameLength);
+ trimmedName[classNameLength] = '\0';
+
+ // attempt to look up the class in the runtime
+ attributes->objectClass = objc_getClass(trimmedName);
+ }
+ }
+
+ if (*next != '\0') {
+ // skip past any junk before the first flag
+ next = strchr(next, ',');
+ }
+
+ while (next && *next == ',') {
+ char flag = next[1];
+ next += 2;
+
+ switch (flag) {
+ case '\0':
+ break;
+
+ case 'R':
+ attributes->readonly = YES;
+ break;
+
+ case 'C':
+ attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyCopy;
+ break;
+
+ case '&':
+ attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyRetain;
+ break;
+
+ case 'N':
+ attributes->nonatomic = YES;
+ break;
+
+ case 'G':
+ case 'S':
+ {
+ const char *nextFlag = strchr(next, ',');
+ SEL name = NULL;
+
+ if (!nextFlag) {
+ // assume that the rest of the string is the selector
+ const char *selectorString = next;
+ next = "";
+
+ name = sel_registerName(selectorString);
+ } else {
+ size_t selectorLength = (size_t)(nextFlag - next);
+ if (!selectorLength) {
+ fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+ goto errorOut;
+ }
+
+ char selectorString[selectorLength + 1];
+
+ strncpy(selectorString, next, selectorLength);
+ selectorString[selectorLength] = '\0';
+
+ name = sel_registerName(selectorString);
+ next = nextFlag;
+ }
+
+ if (flag == 'G')
+ attributes->getter = name;
+ else
+ attributes->setter = name;
+ }
+
+ break;
+
+ case 'D':
+ attributes->dynamic = YES;
+ attributes->ivar = NULL;
+ break;
+
+ case 'V':
+ // assume that the rest of the string (if present) is the ivar name
+ if (*next == '\0') {
+ // if there's nothing there, let's assume this is dynamic
+ attributes->ivar = NULL;
+ } else {
+ attributes->ivar = next;
+ next = "";
+ }
+
+ break;
+
+ case 'W':
+ attributes->weak = YES;
+ break;
+
+ case 'P':
+ attributes->canBeCollected = YES;
+ break;
+
+ case 't':
+ fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
+
+ // skip over this type encoding
+ while (*next != ',' && *next != '\0')
+ ++next;
+
+ break;
+
+ default:
+ fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property));
+ }
+ }
+
+ if (next && *next != '\0') {
+ fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property));
+ }
+
+ if (!attributes->getter) {
+ // use the property name as the getter by default
+ attributes->getter = sel_registerName(property_getName(property));
+ }
+
+ if (!attributes->setter) {
+ const char *propertyName = property_getName(property);
+ size_t propertyNameLength = strlen(propertyName);
+
+ // we want to transform the name to setProperty: style
+ size_t setterLength = propertyNameLength + 4;
+
+ char setterName[setterLength + 1];
+ strncpy(setterName, "set", 3);
+ strncpy(setterName + 3, propertyName, propertyNameLength);
+
+ // capitalize property name for the setter
+ setterName[3] = (char)toupper(setterName[3]);
+
+ setterName[setterLength - 1] = ':';
+ setterName[setterLength] = '\0';
+
+ attributes->setter = sel_registerName(setterName);
+ }
+
+ return attributes;
+
+errorOut:
+ free(attributes);
+ return NULL;
+}
+
+Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector) {
+ unsigned methodCount = 0;
+ Method *methods = class_copyMethodList(aClass, &methodCount);
+ Method foundMethod = NULL;
+
+ for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) {
+ if (method_getName(methods[methodIndex]) == aSelector) {
+ foundMethod = methods[methodIndex];
+ break;
+ }
+ }
+
+ free(methods);
+ return foundMethod;
+}
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTScope.h b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTScope.h
new file mode 100644
index 0000000..2e51747
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/EXTScope.h
@@ -0,0 +1,101 @@
+//
+// EXTScope.h
+// extobjc
+//
+// Created by Justin Spahr-Summers on 2011-05-04.
+// Copyright (C) 2012 Justin Spahr-Summers.
+// Released under the MIT license.
+//
+
+#import "metamacros.h"
+
+/**
+ * \@onExit defines some code to be executed when the current scope exits. The
+ * code must be enclosed in braces and terminated with a semicolon, and will be
+ * executed regardless of how the scope is exited, including from exceptions,
+ * \c goto, \c return, \c break, and \c continue.
+ *
+ * Provided code will go into a block to be executed later. Keep this in mind as
+ * it pertains to memory management, restrictions on assignment, etc. Because
+ * the code is used within a block, \c return is a legal (though perhaps
+ * confusing) way to exit the cleanup block early.
+ *
+ * Multiple \@onExit statements in the same scope are executed in reverse
+ * lexical order. This helps when pairing resource acquisition with \@onExit
+ * statements, as it guarantees teardown in the opposite order of acquisition.
+ *
+ * @note This statement cannot be used within scopes defined without braces
+ * (like a one line \c if). In practice, this is not an issue, since \@onExit is
+ * a useless construct in such a case anyways.
+ */
+#define onExit \
+ autoreleasepool {} \
+ __strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^
+
+/**
+ * Creates \c __weak shadow variables for each of the variables provided as
+ * arguments, which can later be made strong again with #strongify.
+ *
+ * This is typically used to weakly reference variables in a block, but then
+ * ensure that the variables stay alive during the actual execution of the block
+ * (if they were live upon entry).
+ *
+ * See #strongify for an example of usage.
+ */
+#define weakify(...) \
+ autoreleasepool {} \
+ metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
+
+/**
+ * Like #weakify, but uses \c __unsafe_unretained instead, for targets or
+ * classes that do not support weak references.
+ */
+#define unsafeify(...) \
+ autoreleasepool {} \
+ metamacro_foreach_cxt(rac_weakify_,, __unsafe_unretained, __VA_ARGS__)
+
+/**
+ * Strongly references each of the variables provided as arguments, which must
+ * have previously been passed to #weakify.
+ *
+ * The strong references created will shadow the original variable names, such
+ * that the original names can be used without issue (and a significantly
+ * reduced risk of retain cycles) in the current scope.
+ *
+ * @code
+
+ id foo = [[NSObject alloc] init];
+ id bar = [[NSObject alloc] init];
+
+ @weakify(foo, bar);
+
+ // this block will not keep 'foo' or 'bar' alive
+ BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
+ // but now, upon entry, 'foo' and 'bar' will stay alive until the block has
+ // finished executing
+ @strongify(foo, bar);
+
+ return [foo isEqual:obj] || [bar isEqual:obj];
+ };
+
+ * @endcode
+ */
+#define strongify(...) \
+ try {} @finally {} \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wshadow\"") \
+ metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
+ _Pragma("clang diagnostic pop")
+
+/*** implementation details follow ***/
+typedef void (^rac_cleanupBlock_t)();
+
+static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) {
+ (*block)();
+}
+
+#define rac_weakify_(INDEX, CONTEXT, VAR) \
+ CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
+
+#define rac_strongify_(INDEX, VAR) \
+ __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
diff --git a/ReactiveCocoaFramework/ReactiveCocoa/extobjc/metamacros.h b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/metamacros.h
new file mode 100644
index 0000000..77a77b5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoa/extobjc/metamacros.h
@@ -0,0 +1,666 @@
+/**
+ * Macros for metaprogramming
+ * ExtendedC
+ *
+ * Copyright (C) 2012 Justin Spahr-Summers
+ * Released under the MIT license
+ */
+
+#ifndef EXTC_METAMACROS_H
+#define EXTC_METAMACROS_H
+
+/**
+ * Executes one or more expressions (which may have a void type, such as a call
+ * to a function that returns no value) and always returns true.
+ */
+#define metamacro_exprify(...) \
+ ((__VA_ARGS__), true)
+
+/**
+ * Returns a string representation of VALUE after full macro expansion.
+ */
+#define metamacro_stringify(VALUE) \
+ metamacro_stringify_(VALUE)
+
+/**
+ * Returns A and B concatenated after full macro expansion.
+ */
+#define metamacro_concat(A, B) \
+ metamacro_concat_(A, B)
+
+/**
+ * Returns the Nth variadic argument (starting from zero). At least
+ * N + 1 variadic arguments must be given. N must be between zero and twenty,
+ * inclusive.
+ */
+#define metamacro_at(N, ...) \
+ metamacro_concat(metamacro_at, N)(__VA_ARGS__)
+
+/**
+ * Returns the number of arguments (up to twenty) provided to the macro. At
+ * least one argument must be provided.
+ *
+ * Inspired by P99: http://p99.gforge.inria.fr
+ */
+#define metamacro_argcount(...) \
+ metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
+
+/**
+ * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is
+ * given. Only the index and current argument will thus be passed to MACRO.
+ */
+#define metamacro_foreach(MACRO, SEP, ...) \
+ metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
+
+/**
+ * For each consecutive variadic argument (up to twenty), MACRO is passed the
+ * zero-based index of the current argument, CONTEXT, and then the argument
+ * itself. The results of adjoining invocations of MACRO are then separated by
+ * SEP.
+ *
+ * Inspired by P99: http://p99.gforge.inria.fr
+ */
+#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
+ metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
+
+/**
+ * Identical to #metamacro_foreach_cxt. This can be used when the former would
+ * fail due to recursive macro expansion.
+ */
+#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \
+ metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
+
+/**
+ * In consecutive order, appends each variadic argument (up to twenty) onto
+ * BASE. The resulting concatenations are then separated by SEP.
+ *
+ * This is primarily useful to manipulate a list of macro invocations into instead
+ * invoking a different, possibly related macro.
+ */
+#define metamacro_foreach_concat(BASE, SEP, ...) \
+ metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__)
+
+/**
+ * Iterates COUNT times, each time invoking MACRO with the current index
+ * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO
+ * are then separated by SEP.
+ *
+ * COUNT must be an integer between zero and twenty, inclusive.
+ */
+#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \
+ metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT)
+
+/**
+ * Returns the first argument given. At least one argument must be provided.
+ *
+ * This is useful when implementing a variadic macro, where you may have only
+ * one variadic argument, but no way to retrieve it (for example, because \c ...
+ * always needs to match at least one argument).
+ *
+ * @code
+
+#define varmacro(...) \
+ metamacro_head(__VA_ARGS__)
+
+ * @endcode
+ */
+#define metamacro_head(...) \
+ metamacro_head_(__VA_ARGS__, 0)
+
+/**
+ * Returns every argument except the first. At least two arguments must be
+ * provided.
+ */
+#define metamacro_tail(...) \
+ metamacro_tail_(__VA_ARGS__)
+
+/**
+ * Returns the first N (up to twenty) variadic arguments as a new argument list.
+ * At least N variadic arguments must be provided.
+ */
+#define metamacro_take(N, ...) \
+ metamacro_concat(metamacro_take, N)(__VA_ARGS__)
+
+/**
+ * Removes the first N (up to twenty) variadic arguments from the given argument
+ * list. At least N variadic arguments must be provided.
+ */
+#define metamacro_drop(N, ...) \
+ metamacro_concat(metamacro_drop, N)(__VA_ARGS__)
+
+/**
+ * Decrements VAL, which must be a number between zero and twenty, inclusive.
+ *
+ * This is primarily useful when dealing with indexes and counts in
+ * metaprogramming.
+ */
+#define metamacro_dec(VAL) \
+ metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
+
+/**
+ * Increments VAL, which must be a number between zero and twenty, inclusive.
+ *
+ * This is primarily useful when dealing with indexes and counts in
+ * metaprogramming.
+ */
+#define metamacro_inc(VAL) \
+ metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)
+
+/**
+ * If A is equal to B, the next argument list is expanded; otherwise, the
+ * argument list after that is expanded. A and B must be numbers between zero
+ * and twenty, inclusive. Additionally, B must be greater than or equal to A.
+ *
+ * @code
+
+// expands to true
+metamacro_if_eq(0, 0)(true)(false)
+
+// expands to false
+metamacro_if_eq(0, 1)(true)(false)
+
+ * @endcode
+ *
+ * This is primarily useful when dealing with indexes and counts in
+ * metaprogramming.
+ */
+#define metamacro_if_eq(A, B) \
+ metamacro_concat(metamacro_if_eq, A)(B)
+
+/**
+ * Identical to #metamacro_if_eq. This can be used when the former would fail
+ * due to recursive macro expansion.
+ */
+#define metamacro_if_eq_recursive(A, B) \
+ metamacro_concat(metamacro_if_eq_recursive, A)(B)
+
+/**
+ * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and
+ * twenty, inclusive.
+ *
+ * For the purposes of this test, zero is considered even.
+ */
+#define metamacro_is_even(N) \
+ metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
+
+/**
+ * Returns the logical NOT of B, which must be the number zero or one.
+ */
+#define metamacro_not(B) \
+ metamacro_at(B, 1, 0)
+
+// IMPLEMENTATION DETAILS FOLLOW!
+// Do not write code that depends on anything below this line.
+#define metamacro_stringify_(VALUE) # VALUE
+#define metamacro_concat_(A, B) A ## B
+#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)
+#define metamacro_head_(FIRST, ...) FIRST
+#define metamacro_tail_(FIRST, ...) __VA_ARGS__
+#define metamacro_consume_(...)
+#define metamacro_expand_(...) __VA_ARGS__
+
+// implemented from scratch so that metamacro_concat() doesn't end up nesting
+#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG)
+#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG
+
+// metamacro_at expansions
+#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
+#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
+#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
+
+// metamacro_foreach_cxt expansions
+#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
+#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
+
+#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
+ metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
+ SEP \
+ MACRO(1, CONTEXT, _1)
+
+#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
+ metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
+ SEP \
+ MACRO(2, CONTEXT, _2)
+
+#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
+ metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
+ SEP \
+ MACRO(3, CONTEXT, _3)
+
+#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
+ metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
+ SEP \
+ MACRO(4, CONTEXT, _4)
+
+#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
+ metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
+ SEP \
+ MACRO(5, CONTEXT, _5)
+
+#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
+ metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
+ SEP \
+ MACRO(6, CONTEXT, _6)
+
+#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
+ metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
+ SEP \
+ MACRO(7, CONTEXT, _7)
+
+#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
+ metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
+ SEP \
+ MACRO(8, CONTEXT, _8)
+
+#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
+ SEP \
+ MACRO(9, CONTEXT, _9)
+
+#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+ metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ SEP \
+ MACRO(10, CONTEXT, _10)
+
+#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
+ metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+ SEP \
+ MACRO(11, CONTEXT, _11)
+
+#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
+ metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
+ SEP \
+ MACRO(12, CONTEXT, _12)
+
+#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
+ metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
+ SEP \
+ MACRO(13, CONTEXT, _13)
+
+#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
+ metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
+ SEP \
+ MACRO(14, CONTEXT, _14)
+
+#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
+ metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
+ SEP \
+ MACRO(15, CONTEXT, _15)
+
+#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
+ SEP \
+ MACRO(16, CONTEXT, _16)
+
+#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
+ metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ SEP \
+ MACRO(17, CONTEXT, _17)
+
+#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
+ metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
+ SEP \
+ MACRO(18, CONTEXT, _18)
+
+#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
+ metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
+ SEP \
+ MACRO(19, CONTEXT, _19)
+
+// metamacro_foreach_cxt_recursive expansions
+#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT)
+#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
+
+#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \
+ metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \
+ SEP \
+ MACRO(1, CONTEXT, _1)
+
+#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \
+ metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \
+ SEP \
+ MACRO(2, CONTEXT, _2)
+
+#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
+ metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \
+ SEP \
+ MACRO(3, CONTEXT, _3)
+
+#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
+ metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
+ SEP \
+ MACRO(4, CONTEXT, _4)
+
+#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
+ metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
+ SEP \
+ MACRO(5, CONTEXT, _5)
+
+#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
+ metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
+ SEP \
+ MACRO(6, CONTEXT, _6)
+
+#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
+ metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
+ SEP \
+ MACRO(7, CONTEXT, _7)
+
+#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
+ metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
+ SEP \
+ MACRO(8, CONTEXT, _8)
+
+#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
+ SEP \
+ MACRO(9, CONTEXT, _9)
+
+#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+ metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ SEP \
+ MACRO(10, CONTEXT, _10)
+
+#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
+ metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+ SEP \
+ MACRO(11, CONTEXT, _11)
+
+#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
+ metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
+ SEP \
+ MACRO(12, CONTEXT, _12)
+
+#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
+ metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
+ SEP \
+ MACRO(13, CONTEXT, _13)
+
+#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
+ metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
+ SEP \
+ MACRO(14, CONTEXT, _14)
+
+#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
+ metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
+ SEP \
+ MACRO(15, CONTEXT, _15)
+
+#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
+ SEP \
+ MACRO(16, CONTEXT, _16)
+
+#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
+ metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ SEP \
+ MACRO(17, CONTEXT, _17)
+
+#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
+ metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
+ SEP \
+ MACRO(18, CONTEXT, _18)
+
+#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
+ metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
+ SEP \
+ MACRO(19, CONTEXT, _19)
+
+// metamacro_for_cxt expansions
+#define metamacro_for_cxt0(MACRO, SEP, CONTEXT)
+#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT)
+
+#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt1(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(1, CONTEXT)
+
+#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt2(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(2, CONTEXT)
+
+#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt3(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(3, CONTEXT)
+
+#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt4(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(4, CONTEXT)
+
+#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt5(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(5, CONTEXT)
+
+#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt6(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(6, CONTEXT)
+
+#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt7(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(7, CONTEXT)
+
+#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt8(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(8, CONTEXT)
+
+#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt9(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(9, CONTEXT)
+
+#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt10(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(10, CONTEXT)
+
+#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt11(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(11, CONTEXT)
+
+#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt12(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(12, CONTEXT)
+
+#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt13(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(13, CONTEXT)
+
+#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt14(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(14, CONTEXT)
+
+#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt15(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(15, CONTEXT)
+
+#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt16(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(16, CONTEXT)
+
+#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt17(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(17, CONTEXT)
+
+#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt18(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(18, CONTEXT)
+
+#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \
+ metamacro_for_cxt19(MACRO, SEP, CONTEXT) \
+ SEP \
+ MACRO(19, CONTEXT)
+
+// metamacro_if_eq expansions
+#define metamacro_if_eq0(VALUE) \
+ metamacro_concat(metamacro_if_eq0_, VALUE)
+
+#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_
+#define metamacro_if_eq0_1(...) metamacro_expand_
+#define metamacro_if_eq0_2(...) metamacro_expand_
+#define metamacro_if_eq0_3(...) metamacro_expand_
+#define metamacro_if_eq0_4(...) metamacro_expand_
+#define metamacro_if_eq0_5(...) metamacro_expand_
+#define metamacro_if_eq0_6(...) metamacro_expand_
+#define metamacro_if_eq0_7(...) metamacro_expand_
+#define metamacro_if_eq0_8(...) metamacro_expand_
+#define metamacro_if_eq0_9(...) metamacro_expand_
+#define metamacro_if_eq0_10(...) metamacro_expand_
+#define metamacro_if_eq0_11(...) metamacro_expand_
+#define metamacro_if_eq0_12(...) metamacro_expand_
+#define metamacro_if_eq0_13(...) metamacro_expand_
+#define metamacro_if_eq0_14(...) metamacro_expand_
+#define metamacro_if_eq0_15(...) metamacro_expand_
+#define metamacro_if_eq0_16(...) metamacro_expand_
+#define metamacro_if_eq0_17(...) metamacro_expand_
+#define metamacro_if_eq0_18(...) metamacro_expand_
+#define metamacro_if_eq0_19(...) metamacro_expand_
+#define metamacro_if_eq0_20(...) metamacro_expand_
+
+#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))
+#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE))
+#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE))
+#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE))
+#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE))
+#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE))
+#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE))
+#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE))
+#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE))
+#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE))
+#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE))
+#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE))
+#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE))
+#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE))
+#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE))
+#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE))
+#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE))
+#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE))
+#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE))
+#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE))
+
+// metamacro_if_eq_recursive expansions
+#define metamacro_if_eq_recursive0(VALUE) \
+ metamacro_concat(metamacro_if_eq_recursive0_, VALUE)
+
+#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_
+#define metamacro_if_eq_recursive0_1(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_2(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_3(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_4(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_5(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_6(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_7(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_8(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_9(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_10(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_11(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_12(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_13(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_14(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_15(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_16(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_17(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_18(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_19(...) metamacro_expand_
+#define metamacro_if_eq_recursive0_20(...) metamacro_expand_
+
+#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE))
+#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE))
+
+// metamacro_take expansions
+#define metamacro_take0(...)
+#define metamacro_take1(...) metamacro_head(__VA_ARGS__)
+#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__))
+#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__))
+#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__))
+#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__))
+#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__))
+#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__))
+#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__))
+#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__))
+#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__))
+#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__))
+#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__))
+#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__))
+#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__))
+#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__))
+#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__))
+#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__))
+#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__))
+#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__))
+#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__))
+
+// metamacro_drop expansions
+#define metamacro_drop0(...) __VA_ARGS__
+#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__)
+#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__))
+#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__))
+
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSControlRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSControlRACSupportSpec.m
new file mode 100644
index 0000000..1825277
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSControlRACSupportSpec.m
@@ -0,0 +1,106 @@
+//
+// NSControlRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/4/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACControlCommandExamples.h"
+
+#import "NSControl+RACCommandSupport.h"
+#import "NSControl+RACTextSignalSupport.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSubject.h"
+
+SpecBegin(NSControlRACSupport)
+
+describe(@"NSButton", ^{
+ __block NSButton *button;
+
+ beforeEach(^{
+ button = [[NSButton alloc] initWithFrame:NSZeroRect];
+ expect(button).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACControlCommandExamples, ^{
+ return @{
+ RACControlCommandExampleControl: button,
+ RACControlCommandExampleActivateBlock: ^(NSButton *button) {
+ [button performClick:nil];
+ }
+ };
+ });
+});
+
+describe(@"NSTextField", ^{
+ __block NSTextField *field;
+ __block NSWindow *window;
+
+ beforeEach(^{
+ field = [[NSTextField alloc] initWithFrame:NSZeroRect];
+ expect(field).notTo.beNil();
+
+ [field.cell setSendsActionOnEndEditing:YES];
+
+ window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
+ expect(window).notTo.beNil();
+
+ [window.contentView addSubview:field];
+
+ expect([window makeFirstResponder:field]).to.beTruthy();
+ expect(window.firstResponder).notTo.equal(window);
+ });
+
+ itShouldBehaveLike(RACControlCommandExamples, ^{
+ return @{
+ RACControlCommandExampleControl: field,
+ RACControlCommandExampleActivateBlock: ^(NSTextField *field) {
+ expect([window makeFirstResponder:nil]).to.beTruthy();
+ expect(window.firstResponder).to.equal(window);
+ }
+ };
+ });
+
+ describe(@"-rac_textSignal", ^{
+ it(@"should send changes", ^{
+ NSMutableArray *strings = [NSMutableArray array];
+ [field.rac_textSignal subscribeNext:^(NSString *str) {
+ [strings addObject:str];
+ }];
+
+ expect(strings).to.equal(@[ @"" ]);
+
+ NSText *fieldEditor = (id)window.firstResponder;
+ expect(fieldEditor).to.beKindOf(NSText.class);
+
+ [fieldEditor insertText:@"f"];
+ [fieldEditor insertText:@"o"];
+ [fieldEditor insertText:@"b"];
+
+ NSArray *expected = @[ @"", @"f", @"fo", @"fob" ];
+ expect(strings).to.equal(expected);
+ });
+
+ it(@"shouldn't give the text field eternal life", ^{
+ __block BOOL dealloced = NO;
+ @autoreleasepool {
+ NSTextField *field __attribute__((objc_precise_lifetime)) = [[NSTextField alloc] initWithFrame:CGRectZero];
+ [field.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ dealloced = YES;
+ }]];
+ [field.rac_textSignal subscribeNext:^(id x) {
+
+ }];
+ }
+
+ expect(dealloced).will.beTruthy();
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSControllerRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSControllerRACSupportSpec.m
new file mode 100644
index 0000000..d309b25
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSControllerRACSupportSpec.m
@@ -0,0 +1,44 @@
+//
+// NSControllerRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 26/10/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "RACKVOChannel.h"
+
+@interface RACTestController : NSController
+
+@property (nonatomic, strong) id object;
+
+@end
+
+@implementation RACTestController
+
+@end
+
+SpecBegin(NSControllerRACSupport)
+
+it(@"RACKVOChannel should support NSController", ^{
+ RACTestController *a = [[RACTestController alloc] init];
+ RACTestController *b = [[RACTestController alloc] init];
+ RACChannelTo(a, object) = RACChannelTo(b, object);
+ expect(a.object).to.beNil();
+ expect(b.object).to.beNil();
+
+ a.object = a;
+ expect(a.object).to.equal(a);
+ expect(b.object).to.equal(a);
+
+ b.object = b;
+ expect(a.object).to.equal(b);
+ expect(b.object).to.equal(b);
+
+ a.object = nil;
+ expect(a.object).to.beNil();
+ expect(b.object).to.beNil();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSEnumeratorRACSequenceAdditionsSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSEnumeratorRACSequenceAdditionsSpec.m
new file mode 100644
index 0000000..50328bd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSEnumeratorRACSequenceAdditionsSpec.m
@@ -0,0 +1,25 @@
+//
+// NSEnumeratorRACSequenceAdditionsSpec.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 07/01/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequenceExamples.h"
+
+#import "NSEnumerator+RACSequenceAdditions.h"
+
+SpecBegin(NSEnumeratorRACSequenceAdditions)
+
+describe(@"-rac_sequence", ^{
+ NSArray *values = @[ @0, @1, @2, @3, @4 ];
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: values.objectEnumerator.rac_sequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSNotificationCenterRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSNotificationCenterRACSupportSpec.m
new file mode 100644
index 0000000..26143df
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSNotificationCenterRACSupportSpec.m
@@ -0,0 +1,84 @@
+//
+// NSNotificationCenterRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-12-07.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSNotificationCenter+RACSupport.h"
+#import "RACSignal.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "NSObject+RACDeallocating.h"
+
+static NSString * const TestNotification = @"TestNotification";
+
+SpecBegin(NSNotificationCenterRACSupport)
+
+__block NSNotificationCenter *notificationCenter;
+
+beforeEach(^{
+ // The compiler gets confused and thinks you might be messaging
+ // NSDistributedNotificationCenter otherwise. Wtf?
+ notificationCenter = NSNotificationCenter.defaultCenter;
+});
+
+it(@"should send the notification when posted by any object", ^{
+ RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:nil];
+
+ __block NSUInteger count = 0;
+ [signal subscribeNext:^(NSNotification *notification) {
+ ++count;
+
+ expect(notification).to.beKindOf(NSNotification.class);
+ expect(notification.name).to.equal(TestNotification);
+ }];
+
+ expect(count).to.equal(0);
+
+ [notificationCenter postNotificationName:TestNotification object:nil];
+ expect(count).to.equal(1);
+
+ [notificationCenter postNotificationName:TestNotification object:self];
+ expect(count).to.equal(2);
+});
+
+it(@"should send the notification when posted by a specific object", ^{
+ RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:self];
+
+ __block NSUInteger count = 0;
+ [signal subscribeNext:^(NSNotification *notification) {
+ ++count;
+
+ expect(notification).to.beKindOf(NSNotification.class);
+ expect(notification.name).to.equal(TestNotification);
+ expect(notification.object).to.equal(self);
+ }];
+
+ expect(count).to.equal(0);
+
+ [notificationCenter postNotificationName:TestNotification object:nil];
+ expect(count).to.equal(0);
+
+ [notificationCenter postNotificationName:TestNotification object:self];
+ expect(count).to.equal(1);
+});
+
+it(@"shouldn't strongly capture the notification object", ^{
+ RACSignal *signal __attribute__((objc_precise_lifetime, unused));
+
+ __block BOOL dealloced = NO;
+ @autoreleasepool {
+ NSObject *notificationObject = [[NSObject alloc] init];
+ [notificationObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ dealloced = YES;
+ }]];
+
+ signal = [notificationCenter rac_addObserverForName:TestNotification object:notificationObject];
+ }
+
+ expect(dealloced).to.beTruthy();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACAppKitBindingsSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACAppKitBindingsSpec.m
new file mode 100644
index 0000000..fcb4837
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACAppKitBindingsSpec.m
@@ -0,0 +1,36 @@
+//
+// NSObjectRACAppKitBindingsSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACChannelExamples.h"
+
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACAppKitBindings.h"
+
+SpecBegin(NSObjectRACAppKitBindings)
+
+itShouldBehaveLike(RACViewChannelExamples, ^{
+ return @{
+ RACViewChannelExampleCreateViewBlock: ^{
+ return [[NSSlider alloc] initWithFrame:NSZeroRect];
+ },
+ RACViewChannelExampleCreateTerminalBlock: ^(NSSlider *view) {
+ return [view rac_channelToBinding:NSValueBinding];
+ },
+ RACViewChannelExampleKeyPath: @keypath(NSSlider.new, objectValue),
+ RACViewChannelExampleSetViewValueBlock: ^(NSSlider *view, NSNumber *value) {
+ view.objectValue = value;
+
+ // Bindings don't actually trigger from programmatic modification. Do it
+ // manually.
+ NSDictionary *bindingInfo = [view infoForBinding:NSValueBinding];
+ [bindingInfo[NSObservedObjectKey] setValue:value forKeyPath:bindingInfo[NSObservedKeyPathKey]];
+ }
+ };
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACDeallocatingSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACDeallocatingSpec.m
new file mode 100644
index 0000000..b5e80b2
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACDeallocatingSpec.m
@@ -0,0 +1,195 @@
+//
+// NSObject+RACDeallocating.m
+// ReactiveCocoa
+//
+// Created by Kazuo Koga on 2013/03/15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+#import <objc/runtime.h>
+
+@interface RACDeallocSwizzlingTestClass : NSObject
+@end
+
+@implementation RACDeallocSwizzlingTestClass
+
+- (void)dealloc {
+ // Provide an empty implementation just so we can swizzle it.
+}
+
+@end
+
+@interface RACDeallocSwizzlingTestSubclass : RACDeallocSwizzlingTestClass
+@end
+
+@implementation RACDeallocSwizzlingTestSubclass
+@end
+
+SpecBegin(NSObjectRACDeallocatingSpec)
+
+describe(@"-dealloc swizzling", ^{
+ SEL selector = NSSelectorFromString(@"dealloc");
+
+ it(@"should not invoke superclass -dealloc method twice", ^{
+ __block NSUInteger superclassDeallocatedCount = 0;
+ __block BOOL subclassDeallocated = NO;
+
+ @autoreleasepool {
+ RACDeallocSwizzlingTestSubclass *object __attribute__((objc_precise_lifetime)) = [[RACDeallocSwizzlingTestSubclass alloc] init];
+
+ Method oldDeallocMethod = class_getInstanceMethod(RACDeallocSwizzlingTestClass.class, selector);
+ void (*oldDealloc)(id, SEL) = (__typeof__(oldDealloc))method_getImplementation(oldDeallocMethod);
+
+ id newDealloc = ^(__unsafe_unretained id self) {
+ superclassDeallocatedCount++;
+ oldDealloc(self, selector);
+ };
+
+ class_replaceMethod(RACDeallocSwizzlingTestClass.class, selector, imp_implementationWithBlock(newDealloc), method_getTypeEncoding(oldDeallocMethod));
+
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ subclassDeallocated = YES;
+ }]];
+
+ expect(subclassDeallocated).to.beFalsy();
+ expect(superclassDeallocatedCount).to.equal(0);
+ }
+
+ expect(subclassDeallocated).to.beTruthy();
+ expect(superclassDeallocatedCount).to.equal(1);
+ });
+
+ it(@"should invoke superclass -dealloc method swizzled in after the subclass", ^{
+ __block BOOL superclassDeallocated = NO;
+ __block BOOL subclassDeallocated = NO;
+
+ @autoreleasepool {
+ RACDeallocSwizzlingTestSubclass *object __attribute__((objc_precise_lifetime)) = [[RACDeallocSwizzlingTestSubclass alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ subclassDeallocated = YES;
+ }]];
+
+ Method oldDeallocMethod = class_getInstanceMethod(RACDeallocSwizzlingTestClass.class, selector);
+ void (*oldDealloc)(id, SEL) = (__typeof__(oldDealloc))method_getImplementation(oldDeallocMethod);
+
+ id newDealloc = ^(__unsafe_unretained id self) {
+ superclassDeallocated = YES;
+ oldDealloc(self, selector);
+ };
+
+ class_replaceMethod(RACDeallocSwizzlingTestClass.class, selector, imp_implementationWithBlock(newDealloc), method_getTypeEncoding(oldDeallocMethod));
+
+ expect(subclassDeallocated).to.beFalsy();
+ expect(superclassDeallocated).to.beFalsy();
+ }
+
+ expect(subclassDeallocated).to.beTruthy();
+ expect(superclassDeallocated).to.beTruthy();
+ });
+});
+
+describe(@"-rac_deallocDisposable", ^{
+ it(@"should dispose of the disposable when it is dealloc'd", ^{
+ __block BOOL wasDisposed = NO;
+ @autoreleasepool {
+ NSObject *object __attribute__((objc_precise_lifetime)) = [[NSObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ wasDisposed = YES;
+ }]];
+
+ expect(wasDisposed).to.beFalsy();
+ }
+
+ expect(wasDisposed).to.beTruthy();
+ });
+
+ it(@"should be able to use the object during disposal", ^{
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+
+ @autoreleasepool {
+ object.objectValue = [@"foo" mutableCopy];
+ }
+
+ __unsafe_unretained RACTestObject *weakObject = object;
+
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ expect(weakObject.objectValue).to.equal(@"foo");
+ }]];
+ }
+ });
+});
+
+describe(@"-rac_willDeallocSignal", ^{
+ it(@"should complete on dealloc", ^{
+ __block BOOL completed = NO;
+ @autoreleasepool {
+ [[[[RACTestObject alloc] init] rac_willDeallocSignal] subscribeCompleted:^{
+ completed = YES;
+ }];
+ }
+
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should not send anything", ^{
+ __block BOOL valueReceived = NO;
+ __block BOOL completed = NO;
+ @autoreleasepool {
+ [[[[RACTestObject alloc] init] rac_willDeallocSignal] subscribeNext:^(id x) {
+ valueReceived = YES;
+ } completed:^{
+ completed = YES;
+ }];
+ }
+
+ expect(valueReceived).to.beFalsy();
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should complete upon subscription if already deallocated", ^{
+ __block BOOL deallocated = NO;
+
+ RACSignal *signal;
+
+ @autoreleasepool {
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ signal = [object rac_willDeallocSignal];
+ [signal subscribeCompleted:^{
+ deallocated = YES;
+ }];
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect([signal waitUntilCompleted:NULL]).to.beTruthy();
+ });
+
+ it(@"should complete before the object is invalid", ^{
+ __block NSString *objectValue;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+
+ @autoreleasepool {
+ object.objectValue = [@"foo" mutableCopy];
+ }
+
+ __unsafe_unretained RACTestObject *weakObject = object;
+
+ [[object rac_willDeallocSignal] subscribeCompleted:^{
+ objectValue = [weakObject.objectValue copy];
+ }];
+ }
+
+ expect(objectValue).to.equal(@"foo");
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACLiftingSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACLiftingSpec.m
new file mode 100644
index 0000000..7a2acfc
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACLiftingSpec.m
@@ -0,0 +1,407 @@
+//
+// NSObjectRACLifting.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 10/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+
+#import "NSObject+RACLifting.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSubject.h"
+#import "RACTuple.h"
+#import "RACUnit.h"
+
+SpecBegin(NSObjectRACLifting)
+
+describe(@"-rac_liftSelector:withSignals:", ^{
+ __block RACTestObject *object;
+
+ beforeEach(^{
+ object = [[RACTestObject alloc] init];
+ });
+
+ it(@"should call the selector with the value of the signal", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:) withSignals:subject, nil];
+
+ expect(object.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(object.objectValue).to.equal(@1);
+
+ [subject sendNext:@42];
+ expect(object.objectValue).to.equal(@42);
+ });
+});
+
+describe(@"-rac_liftSelector:withSignalsFromArray:", ^{
+ __block RACTestObject *object;
+
+ beforeEach(^{
+ object = [[RACTestObject alloc] init];
+ });
+
+ it(@"should call the selector with the value of the signal", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(object.objectValue).to.equal(@1);
+
+ [subject sendNext:@42];
+ expect(object.objectValue).to.equal(@42);
+ });
+
+ it(@"should call the selector with the value of the signal unboxed", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.integerValue).to.equal(0);
+
+ [subject sendNext:@1];
+ expect(object.integerValue).to.equal(1);
+
+ [subject sendNext:@42];
+ expect(object.integerValue).to.equal(42);
+ });
+
+ it(@"should work with multiple arguments", ^{
+ RACSubject *objectValueSubject = [RACSubject subject];
+ RACSubject *integerValueSubject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectValueSubject, integerValueSubject ]];
+
+ expect(object.hasInvokedSetObjectValueAndIntegerValue).to.beFalsy();
+ expect(object.objectValue).to.beNil();
+ expect(object.integerValue).to.equal(0);
+
+ [objectValueSubject sendNext:@1];
+ expect(object.hasInvokedSetObjectValueAndIntegerValue).to.beFalsy();
+ expect(object.objectValue).to.beNil();
+ expect(object.integerValue).to.equal(0);
+
+ [integerValueSubject sendNext:@42];
+ expect(object.hasInvokedSetObjectValueAndIntegerValue).to.beTruthy();
+ expect(object.objectValue).to.equal(@1);
+ expect(object.integerValue).to.equal(42);
+ });
+
+ it(@"should work with signals that immediately start with a value", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ [subject startWith:@42] ]];
+
+ expect(object.objectValue).to.equal(@42);
+
+ [subject sendNext:@1];
+ expect(object.objectValue).to.equal(@1);
+ });
+
+ it(@"should work with signals that send nil", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
+
+ [subject sendNext:nil];
+ expect(object.objectValue).to.equal(nil);
+
+ [subject sendNext:RACTupleNil.tupleNil];
+ expect(object.objectValue).to.equal(nil);
+ });
+
+ it(@"should work with integers", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.integerValue).to.equal(0);
+
+ [subject sendNext:@1];
+ expect(object.integerValue).to.equal(@1);
+ });
+
+ it(@"should convert between numeric types", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.integerValue).to.equal(0);
+
+ [subject sendNext:@1.0];
+ expect(object.integerValue).to.equal(@1);
+ });
+
+ it(@"should work with class objects", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.objectValue).to.equal(nil);
+
+ [subject sendNext:self.class];
+ expect(object.objectValue).to.equal(self.class);
+ });
+
+ it(@"should work for char pointer", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setCharPointerValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.charPointerValue).to.equal(NULL);
+
+ NSString *string = @"blah blah blah";
+ [subject sendNext:string];
+ expect(@(object.charPointerValue)).to.equal(string);
+ });
+
+ it(@"should work for const char pointer", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setConstCharPointerValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.constCharPointerValue).to.equal(NULL);
+
+ NSString *string = @"blah blah blah";
+ [subject sendNext:string];
+ expect(@(object.constCharPointerValue)).to.equal(string);
+ });
+
+ it(@"should work for CGRect", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setRectValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.rectValue).to.equal(CGRectZero);
+
+ CGRect value = CGRectMake(10, 20, 30, 40);
+ [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGRect)]];
+ expect(object.rectValue).to.equal(value);
+ });
+
+ it(@"should work for CGSize", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setSizeValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.sizeValue).to.equal(CGSizeZero);
+
+ CGSize value = CGSizeMake(10, 20);
+ [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGSize)]];
+ expect(object.sizeValue).to.equal(value);
+ });
+
+ it(@"should work for CGPoint", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setPointValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.pointValue).to.equal(CGPointZero);
+
+ CGPoint value = CGPointMake(10, 20);
+ [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGPoint)]];
+ expect(object.pointValue).to.equal(value);
+ });
+
+ it(@"should work for NSRange", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setRangeValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(NSEqualRanges(object.rangeValue, NSMakeRange(0, 0))).to.beTruthy();
+
+ NSRange value = NSMakeRange(10, 20);
+ [subject sendNext:[NSValue valueWithRange:value]];
+ expect(NSEqualRanges(object.rangeValue, value)).to.beTruthy();
+ });
+
+ it(@"should work for _Bool", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setC99BoolValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.c99BoolValue).to.beFalsy();
+
+ _Bool value = true;
+ [subject sendNext:@(value)];
+ expect(object.c99BoolValue).to.beTruthy();
+ });
+
+ it(@"should work for primitive pointers", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(write5ToIntPointer:) withSignalsFromArray:@[ subject ]];
+
+ int value = 0;
+ int *valuePointer = &value;
+ expect(value).to.equal(0);
+
+ [subject sendNext:[NSValue valueWithPointer:valuePointer]];
+ expect(value).to.equal(5);
+ });
+
+ it(@"should work for custom structs", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setStructValue:) withSignalsFromArray:@[ subject ]];
+
+ expect(object.structValue.integerField).to.equal(0);
+ expect(object.structValue.doubleField).to.equal(0.0);
+
+ RACTestStruct value = (RACTestStruct){7, 1.23};
+ [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(typeof(value))]];
+ expect(object.structValue.integerField).to.equal(value.integerField);
+ expect(object.structValue.doubleField).to.equal(value.doubleField);
+ });
+
+ it(@"should send the latest value of the signal as the right argument", ^{
+ RACSubject *subject = [RACSubject subject];
+ [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ [RACSignal return:@"object"], subject ]];
+ [subject sendNext:@1];
+
+ expect(object.objectValue).to.equal(@"object");
+ expect(object.integerValue).to.equal(1);
+ });
+
+ describe(@"the returned signal", ^{
+ it(@"should send the return value of the method invocation", ^{
+ RACSubject *objectSubject = [RACSubject subject];
+ RACSubject *integerSubject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]];
+
+ __block NSString *result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [objectSubject sendNext:@"Magic number"];
+ expect(result).to.beNil();
+
+ [integerSubject sendNext:@42];
+ expect(result).to.equal(@"Magic number: 42");
+ });
+
+ it(@"should send RACUnit.defaultUnit for void-returning methods", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
+
+ __block id result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [subject sendNext:@1];
+
+ expect(result).to.equal(RACUnit.defaultUnit);
+ });
+
+ it(@"should support integer returning methods", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(doubleInteger:) withSignalsFromArray:@[ subject ]];
+
+ __block id result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [subject sendNext:@1];
+
+ expect(result).to.equal(@2);
+ });
+
+ it(@"should support char * returning methods", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(doubleString:) withSignalsFromArray:@[ subject ]];
+
+ __block id result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [subject sendNext:@"test"];
+
+ expect(result).to.equal(@"testtest");
+ });
+
+ it(@"should support const char * returning methods", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(doubleConstString:) withSignalsFromArray:@[ subject ]];
+
+ __block id result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [subject sendNext:@"test"];
+
+ expect(result).to.equal(@"testtest");
+ });
+
+ it(@"should support struct returning methods", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(doubleStruct:) withSignalsFromArray:@[ subject ]];
+
+ __block NSValue *boxedResult;
+ [signal subscribeNext:^(id x) {
+ boxedResult = x;
+ }];
+
+ RACTestStruct value = {4, 12.3};
+ NSValue *boxedValue = [NSValue valueWithBytes:&value objCType:@encode(typeof(value))];
+ [subject sendNext:boxedValue];
+
+ RACTestStruct result = {0, 0.0};
+ [boxedResult getValue:&result];
+ expect(result.integerField).to.equal(8);
+ expect(result.doubleField).to.equal(24.6);
+ });
+
+ it(@"should support block arguments and returns", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(wrapBlock:) withSignalsFromArray:@[ subject ]];
+
+ __block BOOL blockInvoked = NO;
+ dispatch_block_t testBlock = ^{
+ blockInvoked = YES;
+ };
+
+ __block dispatch_block_t result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ [subject sendNext:testBlock];
+ expect(result).notTo.beNil();
+
+ result();
+ expect(blockInvoked).to.beTruthy();
+ });
+
+ it(@"should replay the last value", ^{
+ RACSubject *objectSubject = [RACSubject subject];
+ RACSubject *integerSubject = [RACSubject subject];
+ RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]];
+
+ [objectSubject sendNext:@"Magic number"];
+ [integerSubject sendNext:@42];
+ [integerSubject sendNext:@43];
+
+ __block NSString *result;
+ [signal subscribeNext:^(id x) {
+ result = x;
+ }];
+
+ expect(result).to.equal(@"Magic number: 43");
+ });
+ });
+
+ it(@"shouldn't strongly capture the receiver", ^{
+ __block BOOL dealloced = NO;
+ @autoreleasepool {
+ RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ dealloced = YES;
+ }]];
+
+ RACSubject *subject = [RACSubject subject];
+ [testObject rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
+ [subject sendNext:@1];
+ }
+
+ expect(dealloced).to.beTruthy();
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.h
new file mode 100644
index 0000000..694ce59
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.h
@@ -0,0 +1,16 @@
+//
+// NSObjectRACPropertySubscribingExamples.h
+// ReactiveCocoa
+//
+// Created by Josh Vera on 4/10/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for a signal-driven observation.
+extern NSString * const RACPropertySubscribingExamples;
+
+// The block should have the signature:
+// RACSignal * (^)(RACTestObject *testObject, NSString *keyPath, id observer)
+// and should observe the value of the key path on testObject with observer. The value
+// for this key should not be nil.
+extern NSString * const RACPropertySubscribingExamplesSetupBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.m
new file mode 100644
index 0000000..1dae113
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.m
@@ -0,0 +1,279 @@
+//
+// NSObjectRACPropertySubscribingExamples.m
+// ReactiveCocoa
+//
+// Created by Josh Vera on 4/10/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+#import "NSObjectRACPropertySubscribingExamples.h"
+
+#import "EXTScope.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+
+NSString * const RACPropertySubscribingExamples = @"RACPropertySubscribingExamples";
+NSString * const RACPropertySubscribingExamplesSetupBlock = @"RACPropertySubscribingExamplesSetupBlock";
+
+SharedExamplesBegin(NSObjectRACPropertySubscribingExamples)
+
+sharedExamples(RACPropertySubscribingExamples, ^(NSDictionary *data) {
+ __block RACSignal *(^signalBlock)(RACTestObject *object, NSString *keyPath, id observer);
+
+ before(^{
+ signalBlock = data[RACPropertySubscribingExamplesSetupBlock];
+ });
+
+ it(@"should send the current value once on subscription", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self);
+ NSMutableArray *values = [NSMutableArray array];
+
+ object.objectValue = @0;
+ [signal subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ expect(values).to.equal((@[ @0 ]));
+ });
+
+ it(@"should send the new value when it changes", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self);
+ NSMutableArray *values = [NSMutableArray array];
+
+ object.objectValue = @0;
+ [signal subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ expect(values).to.equal((@[ @0 ]));
+
+ object.objectValue = @1;
+ expect(values).to.equal((@[ @0, @1 ]));
+
+ });
+
+ it(@"should stop observing when disposed", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self);
+ NSMutableArray *values = [NSMutableArray array];
+
+ object.objectValue = @0;
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ object.objectValue = @1;
+ NSArray *expected = @[ @0, @1 ];
+ expect(values).to.equal(expected);
+
+ [disposable dispose];
+ object.objectValue = @2;
+ expect(values).to.equal(expected);
+ });
+
+ it(@"shouldn't send any more values after the observer is gone", ^{
+ __block BOOL observerDealloced = NO;
+ RACTestObject *object = [[RACTestObject alloc] init];
+ NSMutableArray *values = [NSMutableArray array];
+ @autoreleasepool {
+ RACTestObject *observer __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [observer.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ observerDealloced = YES;
+ }]];
+
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), observer);
+ object.objectValue = @1;
+ [signal subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+ }
+
+ expect(observerDealloced).to.beTruthy();
+
+ NSArray *expected = @[ @1 ];
+ expect(values).to.equal(expected);
+
+ object.objectValue = @2;
+ expect(values).to.equal(expected);
+ });
+
+ it(@"shouldn't keep either object alive unnaturally long", ^{
+ __block BOOL objectDealloced = NO;
+ __block BOOL scopeObjectDealloced = NO;
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ objectDealloced = YES;
+ }]];
+ RACTestObject *scopeObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [scopeObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ scopeObjectDealloced = YES;
+ }]];
+
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), scopeObject);
+
+ [signal subscribeNext:^(id _) {
+
+ }];
+ }
+
+ expect(objectDealloced).to.beTruthy();
+ expect(scopeObjectDealloced).to.beTruthy();
+ });
+
+ it(@"shouldn't keep the signal alive past the lifetime of the object", ^{
+ __block BOOL objectDealloced = NO;
+ __block BOOL signalDealloced = NO;
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ objectDealloced = YES;
+ }]];
+
+ RACSignal *signal = [signalBlock(object, @keypath(object, objectValue), self) map:^(id value) {
+ return value;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ signalDealloced = YES;
+ }]];
+
+ [signal subscribeNext:^(id _) {
+
+ }];
+ }
+
+ expect(signalDealloced).will.beTruthy();
+ expect(objectDealloced).to.beTruthy();
+ });
+
+ it(@"should not resurrect a deallocated object upon subscription", ^{
+ dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
+ @onExit {
+ dispatch_release(queue);
+ };
+
+ dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
+
+ // Fuzz out race conditions.
+ for (unsigned i = 0; i < 100; i++) {
+ dispatch_suspend(queue);
+
+ __block CFTypeRef object;
+ __block BOOL deallocated;
+
+ RACSignal *signal;
+
+ @autoreleasepool {
+ RACTestObject *testObject = [[RACTestObject alloc] init];
+ [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ signal = signalBlock(testObject, @keypath(testObject, objectValue), nil);
+ object = CFBridgingRetain(testObject);
+ }
+
+ dispatch_block_t testSubscription = ^{
+ RACDisposable *disposable = [signal subscribeCompleted:^{}];
+ expect(disposable).notTo.beNil();
+ };
+
+ unsigned beforeCount = arc4random_uniform(20);
+ for (unsigned j = 0; j < beforeCount; j++) {
+ dispatch_async(queue, testSubscription);
+ }
+
+ dispatch_async(queue, ^{
+ CFRelease(object);
+
+ // expect() is a bit finicky on background threads.
+ STAssertTrue(deallocated, @"Object did not deallocate after being released");
+ });
+
+ unsigned afterCount = arc4random_uniform(20);
+ for (unsigned j = 0; j < afterCount; j++) {
+ dispatch_async(queue, testSubscription);
+ }
+
+ dispatch_barrier_async(queue, testSubscription);
+
+ // Start everything and wait for it all to complete.
+ dispatch_resume(queue);
+
+ expect(deallocated).will.beTruthy();
+ dispatch_barrier_sync(queue, ^{});
+ }
+ });
+
+ it(@"shouldn't crash when the value is changed on a different queue", ^{
+ __block id value;
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+
+ RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self);
+
+ [signal subscribeNext:^(id x) {
+ value = x;
+ }];
+
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ [queue addOperationWithBlock:^{
+ object.objectValue = @1;
+ }];
+
+ [queue waitUntilAllOperationsAreFinished];
+ }
+
+ expect(value).will.equal(@1);
+ });
+
+ describe(@"mutating collections", ^{
+ __block RACTestObject *object;
+ __block NSMutableOrderedSet *lastValue;
+ __block NSMutableOrderedSet *proxySet;
+
+ before(^{
+ object = [[RACTestObject alloc] init];
+ object.objectValue = [NSMutableOrderedSet orderedSetWithObject:@1];
+
+ NSString *keyPath = @keypath(object, objectValue);
+
+ [signalBlock(object, keyPath, self) subscribeNext:^(NSMutableOrderedSet *x) {
+ lastValue = x;
+ }];
+
+ proxySet = [object mutableOrderedSetValueForKey:keyPath];
+ });
+
+ it(@"sends the newest object when inserting values into an observed object", ^{
+ NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSetWithObjects: @1, @2, nil];
+
+ [proxySet addObject:@2];
+ expect(lastValue).to.equal(expected);
+ });
+
+ it(@"sends the newest object when removing values in an observed object", ^{
+ NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSet];
+
+ [proxySet removeAllObjects];
+ expect(lastValue).to.equal(expected);
+ });
+
+ it(@"sends the newest object when replacing values in an observed object", ^{
+ NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSetWithObjects: @2, nil];
+
+ [proxySet replaceObjectAtIndex:0 withObject:@2];
+ expect(lastValue).to.equal(expected);
+ });
+ });
+
+});
+
+SharedExamplesEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingSpec.m
new file mode 100644
index 0000000..1b69c2a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingSpec.m
@@ -0,0 +1,155 @@
+//
+// NSObjectRACPropertySubscribingSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObjectRACPropertySubscribingExamples.h"
+#import "RACTestObject.h"
+
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+
+SpecBegin(NSObjectRACPropertySubscribing)
+
+describe(@"-rac_valuesForKeyPath:observer:", ^{
+ id (^setupBlock)(id, id, id) = ^(RACTestObject *object, NSString *keyPath, id observer) {
+ return [object rac_valuesForKeyPath:keyPath observer:observer];
+ };
+
+ itShouldBehaveLike(RACPropertySubscribingExamples, ^{
+ return @{ RACPropertySubscribingExamplesSetupBlock: setupBlock };
+ });
+
+});
+
+describe(@"+rac_signalWithChangesFor:keyPath:options:observer:", ^{
+ describe(@"KVO options argument", ^{
+ __block RACTestObject *object;
+ __block id actual;
+ __block RACSignal *(^objectValueSignal)(NSKeyValueObservingOptions);
+
+ before(^{
+ object = [[RACTestObject alloc] init];
+
+ objectValueSignal = ^(NSKeyValueObservingOptions options) {
+ return [[object rac_valuesAndChangesForKeyPath:@keypath(object, objectValue) options:options observer:self] reduceEach:^(id value, NSDictionary *change) {
+ return change;
+ }];
+ };
+ });
+
+ it(@"sends a KVO dictionary", ^{
+ [objectValueSignal(0) subscribeNext:^(NSDictionary *x) {
+ actual = x;
+ }];
+
+ object.objectValue = @1;
+
+ expect(actual).to.beKindOf(NSDictionary.class);
+ });
+
+ it(@"sends a kind key by default", ^{
+ [objectValueSignal(0) subscribeNext:^(NSDictionary *x) {
+ actual = x[NSKeyValueChangeKindKey];
+ }];
+
+ object.objectValue = @1;
+
+ expect(actual).notTo.beNil();
+ });
+
+ it(@"sends the newest changes with NSKeyValueObservingOptionNew", ^{
+ [objectValueSignal(NSKeyValueObservingOptionNew) subscribeNext:^(NSDictionary *x) {
+ actual = x[NSKeyValueChangeNewKey];
+ }];
+
+ object.objectValue = @1;
+ expect(actual).to.equal(@1);
+
+ object.objectValue = @2;
+ expect(actual).to.equal(@2);
+ });
+
+ it(@"sends an additional change value with NSKeyValueObservingOptionPrior", ^{
+ NSMutableArray *values = [NSMutableArray new];
+ NSArray *expected = @[ @(YES), @(NO) ];
+
+ [objectValueSignal(NSKeyValueObservingOptionPrior) subscribeNext:^(NSDictionary *x) {
+ BOOL isPrior = [x[NSKeyValueChangeNotificationIsPriorKey] boolValue];
+ [values addObject:@(isPrior)];
+ }];
+
+ object.objectValue = @[ @1 ];
+
+ expect(values).to.equal(expected);
+ });
+
+ it(@"sends index changes when adding, inserting or removing a value from an observed object", ^{
+ __block NSUInteger hasIndexesCount = 0;
+
+ [objectValueSignal(0) subscribeNext:^(NSDictionary *x) {
+ if (x[NSKeyValueChangeIndexesKey] != nil) {
+ hasIndexesCount += 1;
+ }
+ }];
+
+ object.objectValue = [NSMutableOrderedSet orderedSet];
+ expect(hasIndexesCount).to.equal(0);
+
+ NSMutableOrderedSet *objectValue = [object mutableOrderedSetValueForKey:@"objectValue"];
+
+ [objectValue addObject:@1];
+ expect(hasIndexesCount).to.equal(1);
+
+ [objectValue replaceObjectAtIndex:0 withObject:@2];
+ expect(hasIndexesCount).to.equal(2);
+
+ [objectValue removeObject:@2];
+ expect(hasIndexesCount).to.equal(3);
+ });
+
+ it(@"sends the previous value with NSKeyValueObservingOptionOld", ^{
+ [objectValueSignal(NSKeyValueObservingOptionOld) subscribeNext:^(NSDictionary *x) {
+ actual = x[NSKeyValueChangeOldKey];
+ }];
+
+ object.objectValue = @1;
+ expect(actual).to.equal(NSNull.null);
+
+ object.objectValue = @2;
+ expect(actual).to.equal(@1);
+ });
+
+ it(@"sends the initial value with NSKeyValueObservingOptionInitial", ^{
+ [objectValueSignal(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) subscribeNext:^(NSDictionary *x) {
+ actual = x[NSKeyValueChangeNewKey];
+ }];
+
+ expect(actual).to.equal(NSNull.null);
+ });
+ });
+});
+
+describe(@"-rac_valuesAndChangesForKeyPath:options:observer:", ^{
+ it(@"should complete immediately if the receiver or observer have deallocated", ^{
+ RACSignal *signal;
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ RACTestObject *observer __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ signal = [object rac_valuesAndChangesForKeyPath:@keypath(object, stringValue) options:0 observer:observer];
+ }
+
+ __block BOOL completed = NO;
+ [signal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(completed).to.beTruthy();
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACSelectorSignalSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACSelectorSignalSpec.m
new file mode 100644
index 0000000..b5b1839
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACSelectorSignalSpec.m
@@ -0,0 +1,434 @@
+//
+// NSObjectRACSelectorSignalSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+#import "RACSubclassObject.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACMulticastConnection.h"
+#import "RACSignal+Operations.h"
+#import "RACSignal.h"
+#import "RACTuple.h"
+
+@protocol TestProtocol
+
+@required
+- (BOOL)requiredMethod:(NSUInteger)number;
+- (void)lifeIsGood:(id)sender;
+
+@optional
+- (NSUInteger)optionalMethodWithObject:(id)object flag:(BOOL)flag;
+- (id)objectValue;
+
+@end
+
+SpecBegin(NSObjectRACSelectorSignal)
+
+describe(@"RACTestObject", ^{
+ it(@"should send the argument for each invocation", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ __block id value;
+ [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ [object lifeIsGood:@42];
+
+ expect(value).to.equal(@42);
+ });
+
+ it(@"should send completed on deallocation", ^{
+ __block BOOL completed = NO;
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(deallocated).to.beFalsy();
+ expect(completed).to.beFalsy();
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should send for a zero-argument method", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block RACTuple *value;
+ [[object rac_signalForSelector:@selector(objectValue)] subscribeNext:^(RACTuple *x) {
+ value = x;
+ }];
+
+ [object objectValue];
+ expect(value).to.equal([RACTuple tupleWithObjectsFromArray:@[]]);
+ });
+
+ it(@"should send the argument for each invocation to the instance's own signal", ^{
+ RACTestObject *object1 = [[RACTestObject alloc] init];
+ __block id value1;
+ [[object1 rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) {
+ value1 = x.first;
+ }];
+
+ RACTestObject *object2 = [[RACTestObject alloc] init];
+ __block id value2;
+ [[object2 rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) {
+ value2 = x.first;
+ }];
+
+ [object1 lifeIsGood:@42];
+ [object2 lifeIsGood:@"Carpe diem"];
+
+ expect(value1).to.equal(@42);
+ expect(value2).to.equal(@"Carpe diem");
+ });
+
+ it(@"should send multiple arguments for each invocation", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id value1;
+ __block id value2;
+ [[object rac_signalForSelector:@selector(combineObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) {
+ value1 = x.first;
+ value2 = x.second;
+ }];
+
+ expect([object combineObjectValue:@42 andSecondObjectValue:@"foo"]).to.equal(@"42: foo");
+ expect(value1).to.equal(@42);
+ expect(value2).to.equal(@"foo");
+ });
+
+ it(@"should send arguments for invocation of non-existant methods", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ __block id key;
+ __block id value;
+ [[object rac_signalForSelector:@selector(setObject:forKey:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ key = x.second;
+ }];
+
+ [object performSelector:@selector(setObject:forKey:) withObject:@YES withObject:@"Winner"];
+
+ expect(value).to.equal(@YES);
+ expect(key).to.equal(@"Winner");
+ });
+
+ it(@"should send arguments for invocation and invoke the original method on previously KVO'd receiver", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ [[RACObserve(object, objectValue) publish] connect];
+
+ __block id key;
+ __block id value;
+ [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ key = x.second;
+ }];
+
+ [object setObjectValue:@YES andSecondObjectValue:@"Winner"];
+
+ expect(object.hasInvokedSetObjectValueAndSecondObjectValue).to.beTruthy();
+ expect(object.objectValue).to.equal(@YES);
+ expect(object.secondObjectValue).to.equal(@"Winner");
+
+ expect(value).to.equal(@YES);
+ expect(key).to.equal(@"Winner");
+ });
+
+ it(@"should send arguments for invocation and invoke the original method when receiver is subsequently KVO'd", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id key;
+ __block id value;
+ [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ key = x.second;
+ }];
+
+ [[RACObserve(object, objectValue) publish] connect];
+
+ [object setObjectValue:@YES andSecondObjectValue:@"Winner"];
+
+ expect(object.hasInvokedSetObjectValueAndSecondObjectValue).to.beTruthy();
+ expect(object.objectValue).to.equal(@YES);
+ expect(object.secondObjectValue).to.equal(@"Winner");
+
+ expect(value).to.equal(@YES);
+ expect(key).to.equal(@"Winner");
+ });
+
+ it(@"should properly implement -respondsToSelector: when called on KVO'd receiver", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ // First, setup KVO on `object`, which gives us the desired side-effect
+ // of `object` taking on a KVO-custom subclass.
+ [[RACObserve(object, objectValue) publish] connect];
+
+ SEL selector = NSSelectorFromString(@"anyOldSelector:");
+
+ // With the KVO subclass in place, call -rac_signalForSelector: to
+ // implement -anyOldSelector: directly on the KVO subclass.
+ [object rac_signalForSelector:selector];
+
+ expect([object respondsToSelector:selector]).to.beTruthy();
+ });
+
+ it(@"should properly implement -respondsToSelector: for optional method from a protocol", ^{
+ // Selector for the targeted optional method from a protocol.
+ SEL selector = @selector(optionalProtocolMethodWithObjectValue:);
+
+ RACTestObject *object1 = [[RACTestObject alloc] init];
+
+ // Method implementation of the selector is added to its swizzled class.
+ [object1 rac_signalForSelector:selector fromProtocol:@protocol(RACTestProtocol)];
+
+ expect([object1 respondsToSelector:selector]).to.beTruthy();
+
+ RACTestObject *object2 = [[RACTestObject alloc] init];
+
+ // Call -rac_signalForSelector: to swizzle this instance's class,
+ // method implementations of -respondsToSelector: and
+ // -forwardInvocation:.
+ [object2 rac_signalForSelector:@selector(lifeIsGood:)];
+
+ // This instance should not respond to the selector because of not
+ // calling -rac_signalForSelector: with the selector.
+ expect([object2 respondsToSelector:selector]).to.beFalsy();
+ });
+
+ it(@"should send non-object arguments", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id value;
+ [[object rac_signalForSelector:@selector(setIntegerValue:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ object.integerValue = 42;
+ expect(value).to.equal(@42);
+ });
+
+ it(@"should send on signal after the original method is invoked", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block BOOL invokedMethodBefore = NO;
+ [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) {
+ invokedMethodBefore = object.hasInvokedSetObjectValueAndSecondObjectValue;
+ }];
+
+ [object setObjectValue:@YES andSecondObjectValue:@"Winner"];
+ expect(invokedMethodBefore).to.beTruthy();
+ });
+});
+
+it(@"should swizzle an NSObject method", ^{
+ NSObject *object = [[NSObject alloc] init];
+
+ __block RACTuple *value;
+ [[object rac_signalForSelector:@selector(description)] subscribeNext:^(RACTuple *x) {
+ value = x;
+ }];
+
+ expect([object description]).notTo.beNil();
+ expect(value).to.equal([RACTuple tupleWithObjectsFromArray:@[]]);
+});
+
+describe(@"a class that already overrides -forwardInvocation:", ^{
+ it(@"should invoke the superclass' implementation", ^{
+ RACSubclassObject *object = [[RACSubclassObject alloc] init];
+
+ __block id value;
+ [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ [object lifeIsGood:@42];
+ expect(value).to.equal(@42);
+
+ expect(object.forwardedSelector).to.beNil();
+
+ [object performSelector:@selector(allObjects)];
+
+ expect(value).to.equal(@42);
+ expect(object.forwardedSelector).to.equal(@selector(allObjects));
+ });
+
+ it(@"should not infinite recurse when KVO'd after RAC swizzled", ^{
+ RACSubclassObject *object = [[RACSubclassObject alloc] init];
+
+ __block id value;
+ [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ [[RACObserve(object, objectValue) publish] connect];
+
+ [object lifeIsGood:@42];
+ expect(value).to.equal(@42);
+
+ expect(object.forwardedSelector).to.beNil();
+ [object performSelector:@selector(allObjects)];
+ expect(object.forwardedSelector).to.equal(@selector(allObjects));
+ });
+});
+
+describe(@"two classes in the same hierarchy", ^{
+ __block RACTestObject *superclassObj;
+ __block RACTuple *superclassTuple;
+
+ __block RACSubclassObject *subclassObj;
+ __block RACTuple *subclassTuple;
+
+ beforeEach(^{
+ superclassObj = [[RACTestObject alloc] init];
+ expect(superclassObj).notTo.beNil();
+
+ subclassObj = [[RACSubclassObject alloc] init];
+ expect(subclassObj).notTo.beNil();
+ });
+
+ it(@"should not collide", ^{
+ [[superclassObj rac_signalForSelector:@selector(combineObjectValue:andIntegerValue:)] subscribeNext:^(RACTuple *t) {
+ superclassTuple = t;
+ }];
+
+ [[subclassObj rac_signalForSelector:@selector(combineObjectValue:andIntegerValue:)] subscribeNext:^(RACTuple *t) {
+ subclassTuple = t;
+ }];
+
+ expect([superclassObj combineObjectValue:@"foo" andIntegerValue:42]).to.equal(@"foo: 42");
+
+ NSArray *expectedValues = @[ @"foo", @42 ];
+ expect(superclassTuple.allObjects).to.equal(expectedValues);
+
+ expect([subclassObj combineObjectValue:@"foo" andIntegerValue:42]).to.equal(@"fooSUBCLASS: 42");
+
+ expectedValues = @[ @"foo", @42 ];
+ expect(subclassTuple.allObjects).to.equal(expectedValues);
+ });
+
+ it(@"should not collide when the superclass is invoked asynchronously", ^{
+ [[superclassObj rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *t) {
+ superclassTuple = t;
+ }];
+
+ [[subclassObj rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *t) {
+ subclassTuple = t;
+ }];
+
+ [superclassObj setObjectValue:@"foo" andSecondObjectValue:@"42"];
+ expect(superclassObj.hasInvokedSetObjectValueAndSecondObjectValue).to.beTruthy();
+
+ NSArray *expectedValues = @[ @"foo", @"42" ];
+ expect(superclassTuple.allObjects).to.equal(expectedValues);
+
+ [subclassObj setObjectValue:@"foo" andSecondObjectValue:@"42"];
+ expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue).to.beFalsy();
+ expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue).will.beTruthy();
+
+ expectedValues = @[ @"foo", @"42" ];
+ expect(subclassTuple.allObjects).to.equal(expectedValues);
+ });
+});
+
+describe(@"-rac_signalForSelector:fromProtocol", ^{
+ __block RACTestObject<TestProtocol> *object;
+ __block Protocol *protocol;
+
+ beforeEach(^{
+ object = (id)[[RACTestObject alloc] init];
+ expect(object).notTo.beNil();
+
+ protocol = @protocol(TestProtocol);
+ expect(protocol).notTo.beNil();
+ });
+
+ it(@"should not clobber a required method already implemented", ^{
+ __block id value;
+ [[object rac_signalForSelector:@selector(lifeIsGood:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ [object lifeIsGood:@42];
+ expect(value).to.equal(@42);
+ });
+
+ it(@"should not clobber an optional method already implemented", ^{
+ object.objectValue = @"foo";
+
+ __block id value;
+ [[object rac_signalForSelector:@selector(objectValue) fromProtocol:protocol] subscribeNext:^(RACTuple *x) {
+ value = x;
+ }];
+
+ expect([object objectValue]).to.equal(@"foo");
+ expect(value).to.equal([RACTuple tupleWithObjectsFromArray:@[]]);
+ });
+
+ it(@"should inject a required method", ^{
+ __block id value;
+ [[object rac_signalForSelector:@selector(requiredMethod:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) {
+ value = x.first;
+ }];
+
+ expect([object requiredMethod:42]).to.beFalsy();
+ expect(value).to.equal(42);
+ });
+
+ it(@"should inject an optional method", ^{
+ __block id value;
+ [[object rac_signalForSelector:@selector(optionalMethodWithObject:flag:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) {
+ value = x;
+ }];
+
+ expect([object optionalMethodWithObject:@"foo" flag:YES]).to.equal(0);
+ expect(value).to.equal(RACTuplePack(@"foo", @YES));
+ });
+});
+
+describe(@"class reporting", ^{
+ __block RACTestObject *object;
+ __block Class originalClass;
+
+ beforeEach(^{
+ object = [[RACTestObject alloc] init];
+ originalClass = object.class;
+ });
+
+ it(@"should report the original class", ^{
+ [object rac_signalForSelector:@selector(lifeIsGood:)];
+ expect(object.class).to.beIdenticalTo(originalClass);
+ });
+
+ it(@"should report the original class when it's KVO'd after dynamically subclassing", ^{
+ [object rac_signalForSelector:@selector(lifeIsGood:)];
+ [[RACObserve(object, objectValue) publish] connect];
+ expect(object.class).to.beIdenticalTo(originalClass);
+ });
+
+ it(@"should report the original class when it's KVO'd before dynamically subclassing", ^{
+ [[RACObserve(object, objectValue) publish] connect];
+ [object rac_signalForSelector:@selector(lifeIsGood:)];
+ expect(object.class).to.beIdenticalTo(originalClass);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSStringRACKeyPathUtilitiesSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSStringRACKeyPathUtilitiesSpec.m
new file mode 100644
index 0000000..72e743a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSStringRACKeyPathUtilitiesSpec.m
@@ -0,0 +1,51 @@
+//
+// NSStringRACKeyPathUtilitiesSpec.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 05/05/2013.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSString+RACKeyPathUtilities.h"
+
+SpecBegin(NSStringRACKeyPathUtilities)
+
+describe(@"-keyPathComponents", ^{
+ it(@"should return components in the key path", ^{
+ expect(@"self.test.key.path".rac_keyPathComponents).to.equal((@[@"self", @"test", @"key", @"path"]));
+ });
+
+ it(@"should return nil if given an empty string", ^{
+ expect(@"".rac_keyPathComponents).to.beNil();
+ });
+});
+
+describe(@"-keyPathByDeletingLastKeyPathComponent", ^{
+ it(@"should return the parent key path", ^{
+ expect(@"grandparent.parent.child".rac_keyPathByDeletingLastKeyPathComponent).to.equal(@"grandparent.parent");
+ });
+
+ it(@"should return nil if given an empty string", ^{
+ expect(@"".rac_keyPathByDeletingLastKeyPathComponent).to.beNil();
+ });
+
+ it(@"should return nil if given a key path with only one component", ^{
+ expect(@"self".rac_keyPathByDeletingLastKeyPathComponent).to.beNil();
+ });
+});
+
+describe(@"-keyPathByDeletingFirstKeyPathComponent", ^{
+ it(@"should return the remaining key path", ^{
+ expect(@"first.second.third".rac_keyPathByDeletingFirstKeyPathComponent).to.equal(@"second.third");
+ });
+
+ it(@"should return nil if given an empty string", ^{
+ expect(@"".rac_keyPathByDeletingFirstKeyPathComponent).to.beNil();
+ });
+
+ it(@"should return nil if given a key path with only one component", ^{
+ expect(@"self".rac_keyPathByDeletingFirstKeyPathComponent).to.beNil();
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSTextRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSTextRACSupportSpec.m
new file mode 100644
index 0000000..364d0e4
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSTextRACSupportSpec.m
@@ -0,0 +1,33 @@
+//
+// NSTextRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-03-08.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSText+RACSignalSupport.h"
+#import "RACSignal.h"
+
+SpecBegin(NSTextRACSupport)
+
+it(@"NSTextView should send changes on rac_textSignal", ^{
+ NSTextView *textView = [[NSTextView alloc] initWithFrame:NSZeroRect];
+ expect(textView).notTo.beNil();
+
+ NSMutableArray *strings = [NSMutableArray array];
+ [textView.rac_textSignal subscribeNext:^(NSString *str) {
+ [strings addObject:str];
+ }];
+
+ expect(strings).to.equal(@[ @"" ]);
+
+ [textView insertText:@"f"];
+ [textView insertText:@"o"];
+ [textView insertText:@"b"];
+
+ NSArray *expected = @[ @"", @"f", @"fo", @"fob" ];
+ expect(strings).to.equal(expected);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSURLConnectionRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSURLConnectionRACSupportSpec.m
new file mode 100644
index 0000000..e7bc8de
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSURLConnectionRACSupportSpec.m
@@ -0,0 +1,36 @@
+//
+// NSURLConnectionRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-10-01.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSURLConnection+RACSupport.h"
+#import "RACSignal+Operations.h"
+#import "RACTuple.h"
+
+SpecBegin(NSURLConnectionRACSupport)
+
+it(@"should fetch a JSON file", ^{
+ NSURL *fileURL = [[NSBundle bundleForClass:self.class] URLForResource:@"test-data" withExtension:@"json"];
+ expect(fileURL).notTo.beNil();
+
+ NSURLRequest *request = [NSURLRequest requestWithURL:fileURL];
+
+ BOOL success = NO;
+ NSError *error = nil;
+ RACTuple *result = [[NSURLConnection rac_sendAsynchronousRequest:request] firstOrDefault:nil success:&success error:&error];
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ expect(result).to.beKindOf(RACTuple.class);
+
+ NSURLResponse *response = result.first;
+ expect(response).to.beKindOf(NSURLResponse.class);
+
+ NSData *data = result.second;
+ expect(data).to.beKindOf(NSData.class);
+ expect(data).to.equal([NSData dataWithContentsOfURL:fileURL]);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/NSUserDefaultsRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/NSUserDefaultsRACSupportSpec.m
new file mode 100644
index 0000000..3f0866c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/NSUserDefaultsRACSupportSpec.m
@@ -0,0 +1,133 @@
+//
+// NSUserDefaultsRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Matt Diephouse on 12/19/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSUserDefaults+RACSupport.h"
+
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOChannel.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACSignal+Operations.h"
+
+static NSString * const NSUserDefaultsRACSupportSpecStringDefault = @"NSUserDefaultsRACSupportSpecStringDefault";
+static NSString * const NSUserDefaultsRACSupportSpecBoolDefault = @"NSUserDefaultsRACSupportSpecBoolDefault";
+
+@interface TestObserver : NSObject
+
+@property (copy, atomic) NSString *string1;
+@property (copy, atomic) NSString *string2;
+
+@property (assign, atomic) BOOL bool1;
+
+@end
+
+@implementation TestObserver
+
+@end
+
+SpecBegin(NSUserDefaultsRACSupportSpec)
+
+__block NSUserDefaults *defaults = nil;
+__block TestObserver *observer = nil;
+
+beforeEach(^{
+ defaults = NSUserDefaults.standardUserDefaults;
+ [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ observer = [TestObserver new];
+});
+
+afterEach(^{
+ observer = nil;
+});
+
+it(@"should set defaults", ^{
+ RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ observer.string1 = @"A string";
+ observer.bool1 = YES;
+
+ expect([defaults objectForKey:NSUserDefaultsRACSupportSpecStringDefault]).will.equal(@"A string");
+ expect([defaults objectForKey:NSUserDefaultsRACSupportSpecBoolDefault]).will.equal(@YES);
+});
+
+it(@"should read defaults", ^{
+ RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ expect(observer.string1).to.beNil();
+ expect(observer.bool1).to.equal(NO);
+
+ [defaults setObject:@"Another string" forKey:NSUserDefaultsRACSupportSpecStringDefault];
+ [defaults setBool:YES forKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ expect(observer.string1).to.equal(@"Another string");
+ expect(observer.bool1).to.equal(YES);
+});
+
+it(@"should be okay to create 2 terminals", ^{
+ RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ RACChannelTo(observer, string2) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+
+ [defaults setObject:@"String 3" forKey:NSUserDefaultsRACSupportSpecStringDefault];
+
+ expect(observer.string1).to.equal(@"String 3");
+ expect(observer.string2).to.equal(@"String 3");
+});
+
+it(@"should handle removed defaults", ^{
+ observer.string1 = @"Some string";
+ observer.bool1 = YES;
+
+ RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault];
+
+ expect(observer.string1).to.beNil();
+ expect(observer.bool1).to.equal(NO);
+});
+
+it(@"shouldn't resend values", ^{
+ RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+
+ RACChannelTo(observer, string1) = terminal;
+
+ RACSignal *sentValue = [terminal replayLast];
+ observer.string1 = @"Test value";
+ id value = [sentValue asynchronousFirstOrDefault:nil success:NULL error:NULL];
+ expect(value).to.beNil();
+});
+
+it(@"should complete when the NSUserDefaults deallocates", ^{
+ __block RACChannelTerminal *terminal;
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ NSUserDefaults *customDefaults __attribute__((objc_precise_lifetime)) = [NSUserDefaults new];
+ [customDefaults.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ terminal = [customDefaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect([terminal asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+});
+
+it(@"should send an initial value", ^{
+ [defaults setObject:@"Initial" forKey:NSUserDefaultsRACSupportSpecStringDefault];
+ RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault];
+ expect([terminal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(@"Initial");
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACBacktraceSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACBacktraceSpec.m
new file mode 100644
index 0000000..9ad4c3b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACBacktraceSpec.m
@@ -0,0 +1,148 @@
+//
+// RACBacktraceSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-12-24.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACBacktrace.h"
+
+#import "NSArray+RACSequenceAdditions.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import "RACSequence.h"
+#import "RACSignal+Operations.h"
+
+#ifdef DEBUG
+
+static RACBacktrace *previousBacktrace;
+
+static void capturePreviousBacktrace(void *context) {
+ previousBacktrace = [RACBacktrace backtrace].previousThreadBacktrace;
+}
+
+typedef struct {
+ dispatch_queue_t queue;
+ NSUInteger i;
+ __unsafe_unretained RACSubject *doneSubject;
+} RACDeepRecursionContext;
+
+static void recurseDeeply(void *ptr) {
+ RACDeepRecursionContext *context = ptr;
+
+ if (context->i++ < 10000) {
+ rac_dispatch_async_f(context->queue, context, recurseDeeply);
+ } else {
+ [context->doneSubject sendCompleted];
+ }
+}
+
+SpecBegin(RACBacktrace)
+
+__block dispatch_block_t block;
+
+beforeEach(^{
+ expect([RACBacktrace backtrace].previousThreadBacktrace).to.beNil();
+ previousBacktrace = nil;
+
+ block = ^{
+ capturePreviousBacktrace(NULL);
+ };
+});
+
+it(@"should capture the current backtrace", ^{
+ RACBacktrace *backtrace = [RACBacktrace backtrace];
+ expect(backtrace).notTo.beNil();
+});
+
+describe(@"with a GCD queue", ^{
+ __block dispatch_queue_t queue;
+
+ beforeEach(^{
+ queue = dispatch_queue_create("com.github.ReactiveCocoa.RACBacktraceSpec", DISPATCH_QUEUE_SERIAL);
+ });
+
+ afterEach(^{
+ dispatch_barrier_sync(queue, ^{});
+ dispatch_release(queue);
+ });
+
+ it(@"should trace across dispatch_async", ^{
+ rac_dispatch_async(queue, block);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_async to the main thread", ^{
+ rac_dispatch_async(queue, ^{
+ rac_dispatch_async(dispatch_get_main_queue(), block);
+ });
+
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_async_f", ^{
+ rac_dispatch_async_f(queue, NULL, &capturePreviousBacktrace);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_barrier_async", ^{
+ rac_dispatch_barrier_async(queue, block);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_barrier_async_f", ^{
+ rac_dispatch_barrier_async_f(queue, NULL, &capturePreviousBacktrace);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_after", ^{
+ rac_dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1), queue, block);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"should trace across dispatch_after_f", ^{
+ rac_dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 1), queue, NULL, &capturePreviousBacktrace);
+ expect(previousBacktrace).willNot.beNil();
+ });
+
+ it(@"shouldn't overflow the stack when deallocating a huge backtrace list", ^{
+ RACSubject *doneSubject = [RACReplaySubject subject];
+ RACDeepRecursionContext context = {
+ .queue = queue,
+ .i = 0,
+ .doneSubject = doneSubject
+ };
+
+ rac_dispatch_async_f(queue, &context, &recurseDeeply);
+ [doneSubject waitUntilCompleted:NULL];
+ });
+});
+
+it(@"should trace across a RACScheduler", ^{
+ [[RACScheduler scheduler] schedule:block];
+ expect(previousBacktrace).willNot.beNil();
+});
+
+it(@"shouldn't go bonkers with RACScheduler", ^{
+ NSMutableArray *a = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 5000; i++) {
+ [a addObject:@(i)];
+ }
+
+ [[a.rac_sequence signalWithScheduler:[RACScheduler scheduler]] subscribeCompleted:^{}];
+});
+
+// Tracing across NSOperationQueue only works on OS X because it depends on
+// interposing through dynamic linking
+#ifndef __IPHONE_OS_VERSION_MIN_REQUIRED
+ it(@"should trace across an NSOperationQueue", ^{
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ [queue addOperationWithBlock:block];
+ expect(previousBacktrace).willNot.beNil();
+ });
+#endif
+
+SpecEnd
+
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACBlockTrampolineSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACBlockTrampolineSpec.m
new file mode 100644
index 0000000..72570fe
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACBlockTrampolineSpec.m
@@ -0,0 +1,48 @@
+//
+// RACBlockTrampolineSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 10/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACBlockTrampoline.h"
+#import "RACTuple.h"
+
+SpecBegin(RACBlockTrampoline)
+
+it(@"should invoke the block with the given arguments", ^{
+ __block NSString *stringArg;
+ __block NSNumber *numberArg;
+ id (^block)(NSString *, NSNumber *) = ^ id (NSString *string, NSNumber *number) {
+ stringArg = string;
+ numberArg = number;
+ return nil;
+ };
+
+ [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi", @1)];
+ expect(stringArg).to.equal(@"hi");
+ expect(numberArg).to.equal(@1);
+});
+
+it(@"should return the result of the block invocation", ^{
+ NSString * (^block)(NSString *) = ^(NSString *string) {
+ return string.uppercaseString;
+ };
+
+ NSString *result = [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi")];
+ expect(result).to.equal(@"HI");
+});
+
+it(@"should pass RACTupleNils as nil", ^{
+ __block id arg;
+ id (^block)(id) = ^ id (id obj) {
+ arg = obj;
+ return nil;
+ };
+
+ [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(nil)];
+ expect(arg).to.beNil();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.h
new file mode 100644
index 0000000..0952fed
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.h
@@ -0,0 +1,34 @@
+//
+// RACChannelExamples.h
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 30/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for RACChannel and its subclasses.
+extern NSString * const RACChannelExamples;
+
+// A block of type `RACChannel * (^)(void)`, which should return a new
+// RACChannel.
+extern NSString * const RACChannelExampleCreateBlock;
+
+// The name of the shared examples for any RACChannel class that gets and sets
+// a property.
+extern NSString * const RACViewChannelExamples;
+
+// A block of type `NSObject * (^)(void)`, which should create a new test view
+// and return it.
+extern NSString * const RACViewChannelExampleCreateViewBlock;
+
+// A block of type `RACChannelTerminal * (^)(NSObject *view)`, which should
+// create a new RACChannel to the given test view and return an terminal.
+extern NSString * const RACViewChannelExampleCreateTerminalBlock;
+
+// The key path that will be read/written in RACViewChannelExamples. This
+// must lead to an NSNumber or numeric primitive property.
+extern NSString * const RACViewChannelExampleKeyPath;
+
+// A block of type `void (^)(NSObject *view, NSNumber *value)`, which should
+// change the given test view's value to the given one.
+extern NSString * const RACViewChannelExampleSetViewValueBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.m
new file mode 100644
index 0000000..9fc77fc
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelExamples.m
@@ -0,0 +1,298 @@
+//
+// RACChannelExamples.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 30/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACChannelExamples.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACChannel.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+
+NSString * const RACChannelExamples = @"RACChannelExamples";
+NSString * const RACChannelExampleCreateBlock = @"RACChannelExampleCreateBlock";
+
+NSString * const RACViewChannelExamples = @"RACViewChannelExamples";
+NSString * const RACViewChannelExampleCreateViewBlock = @"RACViewChannelExampleCreateViewBlock";
+NSString * const RACViewChannelExampleCreateTerminalBlock = @"RACViewChannelExampleCreateTerminalBlock";
+NSString * const RACViewChannelExampleKeyPath = @"RACViewChannelExampleKeyPath";
+NSString * const RACViewChannelExampleSetViewValueBlock = @"RACViewChannelExampleSetViewValueBlock";
+
+SharedExampleGroupsBegin(RACChannelExamples)
+
+sharedExamplesFor(RACChannelExamples, ^(NSDictionary *data) {
+ __block RACChannel * (^getChannel)(void);
+ __block RACChannel *channel;
+
+ id value1 = @"test value 1";
+ id value2 = @"test value 2";
+ id value3 = @"test value 3";
+ NSArray *values = @[ value1, value2, value3 ];
+
+ before(^{
+ getChannel = data[RACChannelExampleCreateBlock];
+ channel = getChannel();
+ });
+
+ it(@"should not send any leadingTerminal value on subscription", ^{
+ __block id receivedValue = nil;
+
+ [channel.followingTerminal sendNext:value1];
+ [channel.leadingTerminal subscribeNext:^(id x) {
+ receivedValue = x;
+ }];
+
+ expect(receivedValue).to.beNil();
+
+ [channel.followingTerminal sendNext:value2];
+ expect(receivedValue).to.equal(value2);
+ });
+
+ it(@"should send the latest followingTerminal value on subscription", ^{
+ __block id receivedValue = nil;
+
+ [channel.leadingTerminal sendNext:value1];
+ [[channel.followingTerminal take:1] subscribeNext:^(id x) {
+ receivedValue = x;
+ }];
+
+ expect(receivedValue).to.equal(value1);
+
+ [channel.leadingTerminal sendNext:value2];
+ [[channel.followingTerminal take:1] subscribeNext:^(id x) {
+ receivedValue = x;
+ }];
+
+ expect(receivedValue).to.equal(value2);
+ });
+
+ it(@"should send leadingTerminal values as they change", ^{
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ [channel.leadingTerminal subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ [channel.followingTerminal sendNext:value1];
+ [channel.followingTerminal sendNext:value2];
+ [channel.followingTerminal sendNext:value3];
+ expect(receivedValues).to.equal(values);
+ });
+
+ it(@"should send followingTerminal values as they change", ^{
+ [channel.leadingTerminal sendNext:value1];
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ [channel.followingTerminal subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ [channel.leadingTerminal sendNext:value2];
+ [channel.leadingTerminal sendNext:value3];
+ expect(receivedValues).to.equal(values);
+ });
+
+ it(@"should complete both signals when the leadingTerminal is completed", ^{
+ __block BOOL completedLeft = NO;
+ [channel.leadingTerminal subscribeCompleted:^{
+ completedLeft = YES;
+ }];
+
+ __block BOOL completedRight = NO;
+ [channel.followingTerminal subscribeCompleted:^{
+ completedRight = YES;
+ }];
+
+ [channel.leadingTerminal sendCompleted];
+ expect(completedLeft).to.beTruthy();
+ expect(completedRight).to.beTruthy();
+ });
+
+ it(@"should complete both signals when the followingTerminal is completed", ^{
+ __block BOOL completedLeft = NO;
+ [channel.leadingTerminal subscribeCompleted:^{
+ completedLeft = YES;
+ }];
+
+ __block BOOL completedRight = NO;
+ [channel.followingTerminal subscribeCompleted:^{
+ completedRight = YES;
+ }];
+
+ [channel.followingTerminal sendCompleted];
+ expect(completedLeft).to.beTruthy();
+ expect(completedRight).to.beTruthy();
+ });
+
+ it(@"should replay completion to new subscribers", ^{
+ [channel.leadingTerminal sendCompleted];
+
+ __block BOOL completedLeft = NO;
+ [channel.leadingTerminal subscribeCompleted:^{
+ completedLeft = YES;
+ }];
+
+ __block BOOL completedRight = NO;
+ [channel.followingTerminal subscribeCompleted:^{
+ completedRight = YES;
+ }];
+
+ expect(completedLeft).to.beTruthy();
+ expect(completedRight).to.beTruthy();
+ });
+});
+
+SharedExampleGroupsEnd
+
+SharedExampleGroupsBegin(RACViewChannelExamples)
+
+sharedExamplesFor(RACViewChannelExamples, ^(NSDictionary *data) {
+ __block NSString *keyPath;
+ __block NSObject * (^getView)(void);
+ __block RACChannelTerminal * (^getTerminal)(NSObject *);
+ __block void (^setViewValue)(NSObject *view, NSNumber *value);
+
+ __block NSObject *testView;
+ __block RACChannelTerminal *endpoint;
+
+ beforeEach(^{
+ keyPath = data[RACViewChannelExampleKeyPath];
+ getTerminal = data[RACViewChannelExampleCreateTerminalBlock];
+ getView = data[RACViewChannelExampleCreateViewBlock];
+ setViewValue = data[RACViewChannelExampleSetViewValueBlock];
+
+ testView = getView();
+ endpoint = getTerminal(testView);
+ });
+
+ it(@"should not send changes made by the channel itself", ^{
+ __block BOOL receivedNext = NO;
+ [endpoint subscribeNext:^(id x) {
+ receivedNext = YES;
+ }];
+
+ expect(receivedNext).to.beFalsy();
+
+ [endpoint sendNext:@0.1];
+ expect(receivedNext).to.beFalsy();
+
+ [endpoint sendNext:@0.2];
+ expect(receivedNext).to.beFalsy();
+
+ [endpoint sendCompleted];
+ expect(receivedNext).to.beFalsy();
+ });
+
+ it(@"should not send progammatic changes made to the view", ^{
+ __block BOOL receivedNext = NO;
+ [endpoint subscribeNext:^(id x) {
+ receivedNext = YES;
+ }];
+
+ expect(receivedNext).to.beFalsy();
+
+ [testView setValue:@0.1 forKeyPath:keyPath];
+ expect(receivedNext).to.beFalsy();
+
+ [testView setValue:@0.2 forKeyPath:keyPath];
+ expect(receivedNext).to.beFalsy();
+ });
+
+ it(@"should not have a starting value", ^{
+ __block BOOL receivedNext = NO;
+ [endpoint subscribeNext:^(id x) {
+ receivedNext = YES;
+ }];
+
+ expect(receivedNext).to.beFalsy();
+ });
+
+ it(@"should send view changes", ^{
+ __block NSString *received;
+ [endpoint subscribeNext:^(id x) {
+ received = x;
+ }];
+
+ setViewValue(testView, @0.1);
+ expect(received).to.equal(@0.1);
+
+ setViewValue(testView, @0.2);
+ expect(received).to.equal(@0.2);
+ });
+
+ it(@"should set values on the view", ^{
+ [endpoint sendNext:@0.1];
+ expect([testView valueForKeyPath:keyPath]).to.equal(@0.1);
+
+ [endpoint sendNext:@0.2];
+ expect([testView valueForKeyPath:keyPath]).to.equal(@0.2);
+ });
+
+ it(@"should not echo changes back to the channel", ^{
+ __block NSUInteger receivedCount = 0;
+ [endpoint subscribeNext:^(id _) {
+ receivedCount++;
+ }];
+
+ expect(receivedCount).to.equal(0);
+
+ [endpoint sendNext:@0.1];
+ expect(receivedCount).to.equal(0);
+
+ setViewValue(testView, @0.2);
+ expect(receivedCount).to.equal(1);
+ });
+
+ it(@"should complete when the view deallocates", ^{
+ __block BOOL deallocated = NO;
+ __block BOOL completed = NO;
+
+ @autoreleasepool {
+ NSObject *view __attribute__((objc_precise_lifetime)) = getView();
+ [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ RACChannelTerminal *terminal = getTerminal(view);
+ [terminal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(deallocated).to.beFalsy();
+ expect(completed).to.beFalsy();
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should deallocate after the view deallocates", ^{
+ __block BOOL viewDeallocated = NO;
+ __block BOOL terminalDeallocated = NO;
+
+ @autoreleasepool {
+ NSObject *view __attribute__((objc_precise_lifetime)) = getView();
+ [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ viewDeallocated = YES;
+ }]];
+
+ RACChannelTerminal *terminal = getTerminal(view);
+ [terminal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ terminalDeallocated = YES;
+ }]];
+
+ expect(viewDeallocated).to.beFalsy();
+ expect(terminalDeallocated).to.beFalsy();
+ }
+
+ expect(viewDeallocated).to.beTruthy();
+ expect(terminalDeallocated).will.beTruthy();
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelSpec.m
new file mode 100644
index 0000000..7bfe6af
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACChannelSpec.m
@@ -0,0 +1,71 @@
+//
+// RACChannelSpec.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 30/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACChannelExamples.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "RACChannel.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+
+SpecBegin(RACChannel)
+
+describe(@"RACChannel", ^{
+ itShouldBehaveLike(RACChannelExamples, @{
+ RACChannelExampleCreateBlock: [^{
+ return [[RACChannel alloc] init];
+ } copy]
+ });
+
+ describe(@"memory management", ^{
+ it(@"should dealloc when its subscribers are disposed", ^{
+ RACDisposable *leadingDisposable = nil;
+ RACDisposable *followingDisposable = nil;
+
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init];
+ [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ leadingDisposable = [channel.leadingTerminal subscribeCompleted:^{}];
+ followingDisposable = [channel.followingTerminal subscribeCompleted:^{}];
+ }
+
+ [leadingDisposable dispose];
+ [followingDisposable dispose];
+ expect(deallocated).will.beTruthy();
+ });
+
+ it(@"should dealloc when its subscriptions are disposed", ^{
+ RACDisposable *leadingDisposable = nil;
+ RACDisposable *followingDisposable = nil;
+
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init];
+ [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ leadingDisposable = [[RACSignal never] subscribe:channel.leadingTerminal];
+ followingDisposable = [[RACSignal never] subscribe:channel.followingTerminal];
+ }
+
+ [leadingDisposable dispose];
+ [followingDisposable dispose];
+ expect(deallocated).will.beTruthy();
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACCommandSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACCommandSpec.m
new file mode 100644
index 0000000..b0c810b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACCommandSpec.m
@@ -0,0 +1,526 @@
+//
+// RACCommandSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 8/31/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSArray+RACSequenceAdditions.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACEvent.h"
+#import "RACScheduler.h"
+#import "RACSequence.h"
+#import "RACSignal+Operations.h"
+#import "RACSubject.h"
+#import "RACUnit.h"
+
+SpecBegin(RACCommand)
+
+RACSignal * (^emptySignalBlock)(id) = ^(id _) {
+ return [RACSignal empty];
+};
+
+describe(@"with a simple signal block", ^{
+ __block RACCommand *command;
+
+ beforeEach(^{
+ command = [[RACCommand alloc] initWithSignalBlock:^(id value) {
+ return [RACSignal return:value];
+ }];
+
+ expect(command).notTo.beNil();
+ expect(command.allowsConcurrentExecution).to.beFalsy();
+ });
+
+ it(@"should be enabled by default", ^{
+ expect([command.enabled first]).to.equal(@YES);
+ });
+
+ it(@"should not be executing by default", ^{
+ expect([command.executing first]).to.equal(@NO);
+ });
+
+ it(@"should create an execution signal", ^{
+ __block NSUInteger signalsReceived = 0;
+ __block BOOL completed = NO;
+
+ id value = NSNull.null;
+ [command.executionSignals subscribeNext:^(RACSignal *signal) {
+ signalsReceived++;
+
+ [signal subscribeNext:^(id x) {
+ expect(x).to.equal(value);
+ } completed:^{
+ completed = YES;
+ }];
+ }];
+
+ expect(signalsReceived).to.equal(0);
+
+ [command execute:value];
+ expect(signalsReceived).will.equal(1);
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should return the execution signal from -execute:", ^{
+ __block BOOL completed = NO;
+
+ id value = NSNull.null;
+ [[command
+ execute:value]
+ subscribeNext:^(id x) {
+ expect(x).to.equal(value);
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(completed).will.beTruthy();
+ });
+
+ it(@"should always send executionSignals on the main thread", ^{
+ __block RACScheduler *receivedScheduler = nil;
+ [command.executionSignals subscribeNext:^(id _) {
+ receivedScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [[RACScheduler scheduler] schedule:^{
+ expect([[command execute:nil] waitUntilCompleted:NULL]).to.beTruthy();
+ }];
+
+ expect(receivedScheduler).to.beNil();
+ expect(receivedScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ });
+
+ it(@"should not send anything on 'errors' by default", ^{
+ __block BOOL receivedError = NO;
+ [command.errors subscribeNext:^(id _) {
+ receivedError = YES;
+ }];
+
+ expect([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect(receivedError).to.beFalsy();
+ });
+
+ it(@"should be executing while an execution signal is running", ^{
+ [command.executionSignals subscribeNext:^(RACSignal *signal) {
+ [signal subscribeNext:^(id x) {
+ expect([command.executing first]).to.equal(@YES);
+ }];
+ }];
+
+ expect([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect([command.executing first]).to.equal(@NO);
+ });
+
+ it(@"should always update executing on the main thread", ^{
+ __block RACScheduler *updatedScheduler = nil;
+ [[command.executing skip:1] subscribeNext:^(NSNumber *executing) {
+ if (!executing.boolValue) return;
+
+ updatedScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [[RACScheduler scheduler] schedule:^{
+ expect([[command execute:nil] waitUntilCompleted:NULL]).to.beTruthy();
+ }];
+
+ expect([command.executing first]).to.equal(@NO);
+ expect(updatedScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ });
+
+ it(@"should dealloc without subscribers", ^{
+ __block BOOL disposed = NO;
+
+ @autoreleasepool {
+ RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithSignalBlock:emptySignalBlock];
+ [command.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }]];
+ }
+
+ expect(disposed).will.beTruthy();
+ });
+
+ it(@"should complete signals on the main thread when deallocated", ^{
+ __block RACScheduler *executionSignalsScheduler = nil;
+ __block RACScheduler *executingScheduler = nil;
+ __block RACScheduler *enabledScheduler = nil;
+ __block RACScheduler *errorsScheduler = nil;
+
+ [[RACScheduler scheduler] schedule:^{
+ @autoreleasepool {
+ RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithSignalBlock:emptySignalBlock];
+
+ [command.executionSignals subscribeCompleted:^{
+ executionSignalsScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [command.executing subscribeCompleted:^{
+ executingScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [command.enabled subscribeCompleted:^{
+ enabledScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [command.errors subscribeCompleted:^{
+ errorsScheduler = RACScheduler.currentScheduler;
+ }];
+ }
+ }];
+
+ expect(executionSignalsScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ expect(executingScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ expect(enabledScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ expect(errorsScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ });
+});
+
+it(@"should invoke the signalBlock once per execution", ^{
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id x) {
+ [valuesReceived addObject:x];
+ return [RACSignal empty];
+ }];
+
+ expect([[command execute:@"foo"] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect(valuesReceived).to.equal((@[ @"foo" ]));
+
+ expect([[command execute:@"bar"] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect(valuesReceived).to.equal((@[ @"foo", @"bar" ]));
+});
+
+it(@"should send on executionSignals in order of execution", ^{
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSequence *seq) {
+ return [seq signalWithScheduler:RACScheduler.immediateScheduler];
+ }];
+
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ [[command.executionSignals
+ concat]
+ subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ }];
+
+ RACSequence *first = @[ @"foo", @"bar" ].rac_sequence;
+ expect([[command execute:first] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+
+ RACSequence *second = @[ @"buzz", @"baz" ].rac_sequence;
+ expect([[command execute:second] asynchronouslyWaitUntilCompleted:NULL]).will.beTruthy();
+
+ NSArray *expectedValues = @[ @"foo", @"bar", @"buzz", @"baz" ];
+ expect(valuesReceived).to.equal(expectedValues);
+});
+
+it(@"should wait for all signals to complete or error before executing sends NO", ^{
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) {
+ return signal;
+ }];
+
+ command.allowsConcurrentExecution = YES;
+
+ RACSubject *firstSubject = [RACSubject subject];
+ expect([command execute:firstSubject]).notTo.beNil();
+
+ RACSubject *secondSubject = [RACSubject subject];
+ expect([command execute:secondSubject]).notTo.beNil();
+
+ expect([command.executing first]).will.equal(@YES);
+
+ [firstSubject sendError:nil];
+ expect([command.executing first]).to.equal(@YES);
+
+ [secondSubject sendNext:nil];
+ expect([command.executing first]).to.equal(@YES);
+
+ [secondSubject sendCompleted];
+ expect([command.executing first]).will.equal(@NO);
+});
+
+it(@"should not deliver errors from executionSignals", ^{
+ RACSubject *subject = [RACSubject subject];
+ NSMutableArray *receivedEvents = [NSMutableArray array];
+
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ return subject;
+ }];
+
+ [[[command.executionSignals
+ flatten]
+ materialize]
+ subscribeNext:^(RACEvent *event) {
+ [receivedEvents addObject:event];
+ }];
+
+ expect([command execute:nil]).notTo.beNil();
+ expect([command.executing first]).will.equal(@YES);
+
+ [subject sendNext:RACUnit.defaultUnit];
+
+ NSArray *expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit] ];
+ expect(receivedEvents).will.equal(expectedEvents);
+ expect([command.executing first]).to.equal(@YES);
+
+ [subject sendNext:@"foo"];
+
+ expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"] ];
+ expect(receivedEvents).will.equal(expectedEvents);
+ expect([command.executing first]).to.equal(@YES);
+
+ NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil];
+ [subject sendError:error];
+
+ expect([command.executing first]).will.equal(@NO);
+ expect(receivedEvents).to.equal(expectedEvents);
+});
+
+it(@"should deliver errors from -execute:", ^{
+ RACSubject *subject = [RACSubject subject];
+ NSMutableArray *receivedEvents = [NSMutableArray array];
+
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ return subject;
+ }];
+
+ [[[command
+ execute:nil]
+ materialize]
+ subscribeNext:^(RACEvent *event) {
+ [receivedEvents addObject:event];
+ }];
+
+ expect([command.executing first]).will.equal(@YES);
+
+ [subject sendNext:RACUnit.defaultUnit];
+
+ NSArray *expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit] ];
+ expect(receivedEvents).will.equal(expectedEvents);
+ expect([command.executing first]).to.equal(@YES);
+
+ [subject sendNext:@"foo"];
+
+ expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"] ];
+ expect(receivedEvents).will.equal(expectedEvents);
+ expect([command.executing first]).to.equal(@YES);
+
+ NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil];
+ [subject sendError:error];
+
+ expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"], [RACEvent eventWithError:error] ];
+ expect(receivedEvents).will.equal(expectedEvents);
+ expect([command.executing first]).will.equal(@NO);
+});
+
+it(@"should deliver errors onto 'errors'", ^{
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) {
+ return signal;
+ }];
+
+ command.allowsConcurrentExecution = YES;
+
+ RACSubject *firstSubject = [RACSubject subject];
+ expect([command execute:firstSubject]).notTo.beNil();
+
+ RACSubject *secondSubject = [RACSubject subject];
+ expect([command execute:secondSubject]).notTo.beNil();
+
+ NSError *firstError = [NSError errorWithDomain:@"" code:1 userInfo:nil];
+ NSError *secondError = [NSError errorWithDomain:@"" code:2 userInfo:nil];
+
+ // We should receive errors from our previously-started executions.
+ NSMutableArray *receivedErrors = [NSMutableArray array];
+ [command.errors subscribeNext:^(NSError *error) {
+ [receivedErrors addObject:error];
+ }];
+
+ expect([command.executing first]).will.equal(@YES);
+
+ [firstSubject sendError:firstError];
+ expect([command.executing first]).will.equal(@YES);
+
+ NSArray *expected = @[ firstError ];
+ expect(receivedErrors).will.equal(expected);
+
+ [secondSubject sendError:secondError];
+ expect([command.executing first]).will.equal(@NO);
+
+ expected = @[ firstError, secondError ];
+ expect(receivedErrors).will.equal(expected);
+});
+
+it(@"should not deliver non-error events onto 'errors'", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ return subject;
+ }];
+
+ __block BOOL receivedEvent = NO;
+ [command.errors subscribeNext:^(id _) {
+ receivedEvent = YES;
+ }];
+
+ expect([command execute:nil]).notTo.beNil();
+ expect([command.executing first]).will.equal(@YES);
+
+ [subject sendNext:RACUnit.defaultUnit];
+ [subject sendCompleted];
+
+ expect([command.executing first]).will.equal(@NO);
+ expect(receivedEvent).to.beFalsy();
+});
+
+it(@"should send errors on the main thread", ^{
+ RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) {
+ return signal;
+ }];
+
+ NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil];
+
+ __block RACScheduler *receivedScheduler = nil;
+ [command.errors subscribeNext:^(NSError *e) {
+ expect(e).to.equal(error);
+ receivedScheduler = RACScheduler.currentScheduler;
+ }];
+
+ RACSignal *errorSignal = [RACSignal error:error];
+
+ [[RACScheduler scheduler] schedule:^{
+ expect([[command execute:errorSignal] waitUntilCompleted:NULL]).to.beTruthy();
+ }];
+
+ expect(receivedScheduler).to.beNil();
+ expect(receivedScheduler).will.equal(RACScheduler.mainThreadScheduler);
+});
+
+describe(@"enabled signal", ^{
+ __block RACSubject *enabledSubject;
+ __block RACCommand *command;
+
+ beforeEach(^{
+ enabledSubject = [RACSubject subject];
+ command = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:^(id _) {
+ return [RACSignal return:RACUnit.defaultUnit];
+ }];
+ });
+
+ it(@"should send YES by default", ^{
+ expect([command.enabled first]).to.equal(@YES);
+ });
+
+ it(@"should send whatever the enabledSignal has sent most recently", ^{
+ [enabledSubject sendNext:@NO];
+ expect([command.enabled first]).will.equal(@NO);
+
+ [enabledSubject sendNext:@YES];
+ expect([command.enabled first]).will.equal(@YES);
+
+ [enabledSubject sendNext:@NO];
+ expect([command.enabled first]).will.equal(@NO);
+ });
+
+ it(@"should sample enabledSignal synchronously at initialization time", ^{
+ RACCommand *command = [[RACCommand alloc] initWithEnabled:[RACSignal return:@NO] signalBlock:^(id _) {
+ return [RACSignal empty];
+ }];
+ expect([command.enabled first]).to.equal(@NO);
+ });
+
+ it(@"should send NO while executing is YES and allowsConcurrentExecution is NO", ^{
+ [[command.executionSignals flatten] subscribeNext:^(id _) {
+ expect([command.executing first]).to.equal(@YES);
+ expect([command.enabled first]).to.equal(@NO);
+ }];
+
+ expect([command.enabled first]).to.equal(@YES);
+ expect([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect([command.enabled first]).to.equal(@YES);
+ });
+
+ it(@"should send YES while executing is YES and allowsConcurrentExecution is YES", ^{
+ command.allowsConcurrentExecution = YES;
+
+ __block BOOL outerExecuted = NO;
+ __block BOOL innerExecuted = NO;
+
+ // Prevent infinite recursion by only responding to the first value.
+ [[[command.executionSignals
+ take:1]
+ flatten]
+ subscribeNext:^(id _) {
+ outerExecuted = YES;
+
+ expect([command.executing first]).to.equal(@YES);
+ expect([command.enabled first]).to.equal(@YES);
+
+ [[command execute:nil] subscribeCompleted:^{
+ innerExecuted = YES;
+ }];
+ }];
+
+ expect([command.enabled first]).to.equal(@YES);
+
+ expect([command execute:nil]).notTo.beNil();
+ expect(outerExecuted).will.beTruthy();
+ expect(innerExecuted).will.beTruthy();
+
+ expect([command.enabled first]).to.equal(@YES);
+ });
+
+ it(@"should send an error from -execute: when NO", ^{
+ [enabledSubject sendNext:@NO];
+
+ RACSignal *signal = [command execute:nil];
+ expect(signal).notTo.beNil();
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ expect([signal firstOrDefault:nil success:&success error:&error]).to.beNil();
+ expect(success).to.beFalsy();
+
+ expect(error).notTo.beNil();
+ expect(error.domain).to.equal(RACCommandErrorDomain);
+ expect(error.code).to.equal(RACCommandErrorNotEnabled);
+ expect(error.userInfo[RACUnderlyingCommandErrorKey]).to.beIdenticalTo(command);
+ });
+
+ it(@"should always update on the main thread", ^{
+ __block RACScheduler *updatedScheduler = nil;
+ [[command.enabled skip:1] subscribeNext:^(id _) {
+ updatedScheduler = RACScheduler.currentScheduler;
+ }];
+
+ [[RACScheduler scheduler] schedule:^{
+ [enabledSubject sendNext:@NO];
+ }];
+
+ expect([command.enabled first]).to.equal(@YES);
+ expect([command.enabled first]).will.equal(@NO);
+ expect(updatedScheduler).to.equal(RACScheduler.mainThreadScheduler);
+ });
+
+ it(@"should complete when the command is deallocated even if the input signal hasn't", ^{
+ __block BOOL deallocated = NO;
+ __block BOOL completed = NO;
+
+ @autoreleasepool {
+ RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:emptySignalBlock];
+ [command.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ [command.enabled subscribeCompleted:^{
+ completed = YES;
+ }];
+ }
+
+ expect(deallocated).will.beTruthy();
+ expect(completed).will.beTruthy();
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACCompoundDisposableSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACCompoundDisposableSpec.m
new file mode 100644
index 0000000..faad3bb
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACCompoundDisposableSpec.m
@@ -0,0 +1,109 @@
+//
+// RACCompoundDisposableSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/30/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACCompoundDisposable.h"
+
+SpecBegin(RACCompoundDisposable)
+
+it(@"should dispose of all its contained disposables", ^{
+ __block BOOL d1Disposed = NO;
+ RACDisposable *d1 = [RACDisposable disposableWithBlock:^{
+ d1Disposed = YES;
+ }];
+
+ __block BOOL d2Disposed = NO;
+ RACDisposable *d2 = [RACDisposable disposableWithBlock:^{
+ d2Disposed = YES;
+ }];
+
+ __block BOOL d3Disposed = NO;
+ RACDisposable *d3 = [RACDisposable disposableWithBlock:^{
+ d3Disposed = YES;
+ }];
+
+ __block BOOL d4Disposed = NO;
+ RACDisposable *d4 = [RACDisposable disposableWithBlock:^{
+ d4Disposed = YES;
+ }];
+
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ d1, d2, d3 ]];
+ [disposable addDisposable:d4];
+
+ expect(d1Disposed).to.beFalsy();
+ expect(d2Disposed).to.beFalsy();
+ expect(d3Disposed).to.beFalsy();
+ expect(d4Disposed).to.beFalsy();
+ expect(disposable.disposed).to.beFalsy();
+
+ [disposable dispose];
+
+ expect(d1Disposed).to.beTruthy();
+ expect(d2Disposed).to.beTruthy();
+ expect(d3Disposed).to.beTruthy();
+ expect(d4Disposed).to.beTruthy();
+ expect(disposable.disposed).to.beTruthy();
+});
+
+it(@"should dispose of any added disposables immediately if it's already been disposed", ^{
+ RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
+ [disposable dispose];
+
+ RACDisposable *d = [[RACDisposable alloc] init];
+
+ expect(d.disposed).to.beFalsy();
+ [disposable addDisposable:d];
+ expect(d.disposed).to.beTruthy();
+});
+
+it(@"should work when initialized with -init", ^{
+ RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init];
+
+ __block BOOL disposed = NO;
+ RACDisposable *d = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ [disposable addDisposable:d];
+ expect(disposed).to.beFalsy();
+
+ [disposable dispose];
+ expect(disposed).to.beTruthy();
+});
+
+it(@"should work when initialized with +disposableWithBlock:", ^{
+ __block BOOL compoundDisposed = NO;
+ RACCompoundDisposable *disposable = [RACCompoundDisposable disposableWithBlock:^{
+ compoundDisposed = YES;
+ }];
+
+ __block BOOL disposed = NO;
+ RACDisposable *d = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ [disposable addDisposable:d];
+ expect(disposed).to.beFalsy();
+ expect(compoundDisposed).to.beFalsy();
+
+ [disposable dispose];
+ expect(disposed).to.beTruthy();
+ expect(compoundDisposed).to.beTruthy();
+});
+
+it(@"should allow disposables to be removed", ^{
+ RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init];
+ RACDisposable *d = [[RACDisposable alloc] init];
+
+ [disposable addDisposable:d];
+ [disposable removeDisposable:d];
+
+ [disposable dispose];
+ expect(d.disposed).to.beFalsy();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.h
new file mode 100644
index 0000000..3fbaa34
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.h
@@ -0,0 +1,18 @@
+//
+// RACControlCommandExamples.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-08-15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for any control class that has
+// `rac_command` and `isEnabled` properties.
+extern NSString * const RACControlCommandExamples;
+
+// The control to test.
+extern NSString * const RACControlCommandExampleControl;
+
+// A block of type `void (^)(id control)` which should activate the
+// `rac_command` of the `control` by manipulating the control itself.
+extern NSString * const RACControlCommandExampleActivateBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.m
new file mode 100644
index 0000000..034cc81
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACControlCommandExamples.m
@@ -0,0 +1,81 @@
+//
+// RACControlCommandExamples.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-08-15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACControlCommandExamples.h"
+
+#import "RACCommand.h"
+#import "RACSubject.h"
+#import "RACUnit.h"
+
+NSString * const RACControlCommandExamples = @"RACControlCommandExamples";
+NSString * const RACControlCommandExampleControl = @"RACControlCommandExampleControl";
+NSString * const RACControlCommandExampleActivateBlock = @"RACControlCommandExampleActivateBlock";
+
+// Methods used by the unit test that would otherwise require platform-specific
+// imports.
+@interface NSObject (RACControlCommandExamples)
+
+@property (nonatomic, strong) RACCommand *rac_command;
+
+- (BOOL)isEnabled;
+
+@end
+
+SharedExampleGroupsBegin(RACControlCommandExamples)
+
+sharedExamplesFor(RACControlCommandExamples, ^(NSDictionary *data) {
+ __block id control;
+ __block void (^activate)(id);
+
+ __block RACSubject *enabledSubject;
+ __block RACCommand *command;
+
+ beforeEach(^{
+ control = data[RACControlCommandExampleControl];
+ activate = [data[RACControlCommandExampleActivateBlock] copy];
+
+ enabledSubject = [RACSubject subject];
+ command = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:^(id sender) {
+ return [RACSignal return:sender];
+ }];
+
+ [control setRac_command:command];
+ });
+
+ it(@"should bind the control's enabledness to the command", ^{
+ expect([control isEnabled]).will.beTruthy();
+
+ [enabledSubject sendNext:@NO];
+ expect([control isEnabled]).will.beFalsy();
+
+ [enabledSubject sendNext:@YES];
+ expect([control isEnabled]).will.beTruthy();
+ });
+
+ it(@"should execute the control's command when activated", ^{
+ __block BOOL executed = NO;
+ [[command.executionSignals flatten] subscribeNext:^(id sender) {
+ expect(sender).to.equal(control);
+ executed = YES;
+ }];
+
+ activate(control);
+ expect(executed).will.beTruthy();
+ });
+
+ it(@"should overwrite an existing command when setting a new one", ^{
+ RACCommand *secondCommand = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ return [RACSignal return:RACUnit.defaultUnit];
+ }];
+
+ [control setRac_command:secondCommand];
+ expect([control rac_command]).to.beIdenticalTo(secondCommand);
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACDelegateProxySpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACDelegateProxySpec.m
new file mode 100644
index 0000000..831c3a3
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACDelegateProxySpec.m
@@ -0,0 +1,89 @@
+//
+// RACDelegateProxySpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACSelectorSignal.h"
+#import "RACDelegateProxy.h"
+#import "RACSignal.h"
+#import "RACTuple.h"
+#import "RACCompoundDisposable.h"
+#import "NSObject+RACDeallocating.h"
+
+@protocol TestDelegateProtocol
+- (NSUInteger)lengthOfString:(NSString *)str;
+@end
+
+@interface TestDelegate : NSObject <TestDelegateProtocol>
+@property (nonatomic, assign) BOOL lengthOfStringInvoked;
+@end
+
+SpecBegin(RACDelegateProxy)
+
+__block id proxy;
+__block TestDelegate *delegate;
+__block Protocol *protocol;
+
+beforeEach(^{
+ protocol = @protocol(TestDelegateProtocol);
+ expect(protocol).notTo.beNil();
+
+ proxy = [[RACDelegateProxy alloc] initWithProtocol:protocol];
+ expect(proxy).notTo.beNil();
+ expect([proxy rac_proxiedDelegate]).to.beNil();
+
+ delegate = [[TestDelegate alloc] init];
+ expect(delegate).notTo.beNil();
+});
+
+it(@"should not respond to selectors at first", ^{
+ expect([proxy respondsToSelector:@selector(lengthOfString:)]).to.beFalsy();
+});
+
+it(@"should send on a signal for a protocol method", ^{
+ __block RACTuple *tuple;
+ [[proxy signalForSelector:@selector(lengthOfString:)] subscribeNext:^(RACTuple *t) {
+ tuple = t;
+ }];
+
+ expect([proxy respondsToSelector:@selector(lengthOfString:)]).to.beTruthy();
+ expect([proxy lengthOfString:@"foo"]).to.equal(0);
+ expect(tuple).to.equal(RACTuplePack(@"foo"));
+});
+
+it(@"should forward to the proxied delegate", ^{
+ [proxy setRac_proxiedDelegate:delegate];
+
+ expect([proxy respondsToSelector:@selector(lengthOfString:)]).to.beTruthy();
+ expect([proxy lengthOfString:@"foo"]).to.equal(3);
+ expect(delegate.lengthOfStringInvoked).to.beTruthy();
+});
+
+it(@"should not send to the delegate when signals are applied", ^{
+ [proxy setRac_proxiedDelegate:delegate];
+
+ __block RACTuple *tuple;
+ [[proxy signalForSelector:@selector(lengthOfString:)] subscribeNext:^(RACTuple *t) {
+ tuple = t;
+ }];
+
+ expect([proxy respondsToSelector:@selector(lengthOfString:)]).to.beTruthy();
+ expect([proxy lengthOfString:@"foo"]).to.equal(0);
+
+ expect(tuple).to.equal(RACTuplePack(@"foo"));
+ expect(delegate.lengthOfStringInvoked).to.beFalsy();
+});
+
+SpecEnd
+
+@implementation TestDelegate
+
+- (NSUInteger)lengthOfString:(NSString *)str {
+ self.lengthOfStringInvoked = YES;
+ return str.length;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACDisposableSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACDisposableSpec.m
new file mode 100644
index 0000000..8cde917
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACDisposableSpec.m
@@ -0,0 +1,73 @@
+//
+// RACDisposableSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACDisposable.h"
+#import "RACScopedDisposable.h"
+
+SpecBegin(RACDisposable)
+
+it(@"should initialize without a block", ^{
+ RACDisposable *disposable = [[RACDisposable alloc] init];
+ expect(disposable).notTo.beNil();
+ expect(disposable.disposed).to.beFalsy();
+
+ [disposable dispose];
+ expect(disposable.disposed).to.beTruthy();
+});
+
+it(@"should execute a block upon disposal", ^{
+ __block BOOL disposed = NO;
+ RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+ expect(disposed).to.beFalsy();
+ expect(disposable.disposed).to.beFalsy();
+
+ [disposable dispose];
+ expect(disposed).to.beTruthy();
+ expect(disposable.disposed).to.beTruthy();
+});
+
+it(@"should not dispose upon deallocation", ^{
+ __block BOOL disposed = NO;
+ __weak RACDisposable *weakDisposable = nil;
+
+ @autoreleasepool {
+ RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ weakDisposable = disposable;
+ expect(weakDisposable).notTo.beNil();
+ }
+
+ expect(weakDisposable).to.beNil();
+ expect(disposed).to.beFalsy();
+});
+
+it(@"should create a scoped disposable", ^{
+ __block BOOL disposed = NO;
+ __weak RACScopedDisposable *weakDisposable = nil;
+
+ @autoreleasepool {
+ RACScopedDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACScopedDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ weakDisposable = disposable;
+ expect(weakDisposable).notTo.beNil();
+ expect(disposed).to.beFalsy();
+ }
+
+ expect(weakDisposable).to.beNil();
+ expect(disposed).to.beTruthy();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACEventSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACEventSpec.m
new file mode 100644
index 0000000..9545fb6
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACEventSpec.m
@@ -0,0 +1,80 @@
+//
+// RACEventSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-01-07.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACEvent.h"
+
+SpecBegin(RACEvent)
+
+it(@"should return the singleton completed event", ^{
+ RACEvent *event = RACEvent.completedEvent;
+ expect(event).notTo.beNil();
+
+ expect(event).to.beIdenticalTo(RACEvent.completedEvent);
+ expect([event copy]).to.beIdenticalTo(event);
+
+ expect(event.eventType).to.equal(RACEventTypeCompleted);
+ expect(event.finished).to.beTruthy();
+ expect(event.error).to.beNil();
+ expect(event.value).to.beNil();
+});
+
+it(@"should return an error event", ^{
+ NSError *error = [NSError errorWithDomain:@"foo" code:1 userInfo:nil];
+ RACEvent *event = [RACEvent eventWithError:error];
+ expect(event).notTo.beNil();
+
+ expect(event).to.equal([RACEvent eventWithError:error]);
+ expect([event copy]).to.equal(event);
+
+ expect(event.eventType).to.equal(RACEventTypeError);
+ expect(event.finished).to.beTruthy();
+ expect(event.error).to.equal(error);
+ expect(event.value).to.beNil();
+});
+
+it(@"should return an error event with a nil error", ^{
+ RACEvent *event = [RACEvent eventWithError:nil];
+ expect(event).notTo.beNil();
+
+ expect(event).to.equal([RACEvent eventWithError:nil]);
+ expect([event copy]).to.equal(event);
+
+ expect(event.eventType).to.equal(RACEventTypeError);
+ expect(event.finished).to.beTruthy();
+ expect(event.error).to.beNil();
+ expect(event.value).to.beNil();
+});
+
+it(@"should return a next event", ^{
+ NSString *value = @"foo";
+ RACEvent *event = [RACEvent eventWithValue:value];
+ expect(event).notTo.beNil();
+
+ expect(event).to.equal([RACEvent eventWithValue:value]);
+ expect([event copy]).to.equal(event);
+
+ expect(event.eventType).to.equal(RACEventTypeNext);
+ expect(event.finished).to.beFalsy();
+ expect(event.error).to.beNil();
+ expect(event.value).to.equal(value);
+});
+
+it(@"should return a next event with a nil value", ^{
+ RACEvent *event = [RACEvent eventWithValue:nil];
+ expect(event).notTo.beNil();
+
+ expect(event).to.equal([RACEvent eventWithValue:nil]);
+ expect([event copy]).to.equal(event);
+
+ expect(event.eventType).to.equal(RACEventTypeNext);
+ expect(event.finished).to.beFalsy();
+ expect(event.error).to.beNil();
+ expect(event.value).to.beNil();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOChannelSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOChannelSpec.m
new file mode 100644
index 0000000..8007511
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOChannelSpec.m
@@ -0,0 +1,389 @@
+//
+// RACKVOChannelSpec.m
+// ReactiveCocoa
+//
+// Created by Uri Baghin on 16/12/2012.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+#import "RACChannelExamples.h"
+#import "RACPropertySignalExamples.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACKVOWrapper.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOChannel.h"
+#import "RACSignal+Operations.h"
+
+SpecBegin(RACKVOChannel)
+
+describe(@"RACKVOChannel", ^{
+ __block RACTestObject *object;
+ __block RACKVOChannel *channel;
+ id value1 = @"test value 1";
+ id value2 = @"test value 2";
+ id value3 = @"test value 3";
+ NSArray *values = @[ value1, value2, value3 ];
+
+ before(^{
+ object = [[RACTestObject alloc] init];
+ channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil];
+ });
+
+ id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) {
+ RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:testObject keyPath:keyPath nilValue:nilValue];
+ [signal subscribe:channel.followingTerminal];
+ };
+
+ itShouldBehaveLike(RACPropertySignalExamples, ^{
+ return @{ RACPropertySignalExamplesSetupBlock: setupBlock };
+ });
+
+ itShouldBehaveLike(RACChannelExamples, @{
+ RACChannelExampleCreateBlock: [^{
+ return [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil];
+ } copy]
+ });
+
+ it(@"should send the object's current value when subscribed to followingTerminal", ^{
+ __block id receivedValue = @"received value should not be this";
+ [[channel.followingTerminal take:1] subscribeNext:^(id x) {
+ receivedValue = x;
+ }];
+
+ expect(receivedValue).to.beNil();
+
+ object.stringValue = value1;
+ [[channel.followingTerminal take:1] subscribeNext:^(id x) {
+ receivedValue = x;
+ }];
+
+ expect(receivedValue).to.equal(value1);
+ });
+
+ it(@"should send the object's new value on followingTerminal when it's changed", ^{
+ object.stringValue = value1;
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ [channel.followingTerminal subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ object.stringValue = value2;
+ object.stringValue = value3;
+ expect(receivedValues).to.equal(values);
+ });
+
+ it(@"should set the object's value using values sent to the followingTerminal", ^{
+ expect(object.stringValue).to.beNil();
+
+ [channel.followingTerminal sendNext:value1];
+ expect(object.stringValue).to.equal(value1);
+
+ [channel.followingTerminal sendNext:value2];
+ expect(object.stringValue).to.equal(value2);
+ });
+
+ it(@"should be able to subscribe to signals", ^{
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ [object rac_observeKeyPath:@keypath(object.stringValue) options:0 observer:self block:^(id value, NSDictionary *change) {
+ [receivedValues addObject:value];
+ }];
+
+ RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:value1];
+ [subscriber sendNext:value2];
+ [subscriber sendNext:value3];
+ return nil;
+ }];
+
+ [signal subscribe:channel.followingTerminal];
+ expect(receivedValues).to.equal(values);
+ });
+
+ it(@"should complete both terminals when the target deallocates", ^{
+ __block BOOL leadingCompleted = NO;
+ __block BOOL followingCompleted = NO;
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil];
+ [channel.leadingTerminal subscribeCompleted:^{
+ leadingCompleted = YES;
+ }];
+
+ [channel.followingTerminal subscribeCompleted:^{
+ followingCompleted = YES;
+ }];
+
+ expect(deallocated).to.beFalsy();
+ expect(leadingCompleted).to.beFalsy();
+ expect(followingCompleted).to.beFalsy();
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect(leadingCompleted).to.beTruthy();
+ expect(followingCompleted).to.beTruthy();
+ });
+
+ it(@"should deallocate when the target deallocates", ^{
+ __block BOOL targetDeallocated = NO;
+ __block BOOL channelDeallocated = NO;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ targetDeallocated = YES;
+ }]];
+
+ RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil];
+ [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ channelDeallocated = YES;
+ }]];
+
+ expect(targetDeallocated).to.beFalsy();
+ expect(channelDeallocated).to.beFalsy();
+ }
+
+ expect(targetDeallocated).to.beTruthy();
+ expect(channelDeallocated).to.beTruthy();
+ });
+});
+
+describe(@"RACChannelTo", ^{
+ __block RACTestObject *a;
+ __block RACTestObject *b;
+ __block RACTestObject *c;
+ __block NSString *testName1;
+ __block NSString *testName2;
+ __block NSString *testName3;
+
+ before(^{
+ a = [[RACTestObject alloc] init];
+ b = [[RACTestObject alloc] init];
+ c = [[RACTestObject alloc] init];
+ testName1 = @"sync it!";
+ testName2 = @"sync it again!";
+ testName3 = @"sync it once more!";
+ });
+
+ it(@"should keep objects' properties in sync", ^{
+ RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue);
+ expect(a.stringValue).to.beNil();
+ expect(b.stringValue).to.beNil();
+
+ a.stringValue = testName1;
+ expect(a.stringValue).to.equal(testName1);
+ expect(b.stringValue).to.equal(testName1);
+
+ b.stringValue = testName2;
+ expect(a.stringValue).to.equal(testName2);
+ expect(b.stringValue).to.equal(testName2);
+
+ a.stringValue = nil;
+ expect(a.stringValue).to.beNil();
+ expect(b.stringValue).to.beNil();
+ });
+
+ it(@"should keep properties identified by keypaths in sync", ^{
+ RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue);
+ a.strongTestObjectValue = [[RACTestObject alloc] init];
+ b.strongTestObjectValue = [[RACTestObject alloc] init];
+
+ a.strongTestObjectValue.stringValue = testName1;
+ expect(a.strongTestObjectValue.stringValue).to.equal(testName1);
+ expect(b.strongTestObjectValue.stringValue).to.equal(testName1);
+ expect(a.strongTestObjectValue).notTo.equal(b.strongTestObjectValue);
+
+ b.strongTestObjectValue = nil;
+ expect(a.strongTestObjectValue.stringValue).to.beNil();
+
+ c.stringValue = testName2;
+ b.strongTestObjectValue = c;
+ expect(a.strongTestObjectValue.stringValue).to.equal(testName2);
+ expect(b.strongTestObjectValue.stringValue).to.equal(testName2);
+ expect(a.strongTestObjectValue).notTo.equal(b.strongTestObjectValue);
+ });
+
+ it(@"should update properties identified by keypaths when the intermediate values change", ^{
+ RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue);
+ a.strongTestObjectValue = [[RACTestObject alloc] init];
+ b.strongTestObjectValue = [[RACTestObject alloc] init];
+ c.stringValue = testName1;
+ b.strongTestObjectValue = c;
+
+ expect(a.strongTestObjectValue.stringValue).to.equal(testName1);
+ expect(a.strongTestObjectValue).notTo.equal(b.strongTestObjectValue);
+ });
+
+ it(@"should update properties identified by keypaths when the channel was created when one of the two objects had an intermediate nil value", ^{
+ RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue);
+ b.strongTestObjectValue = [[RACTestObject alloc] init];
+ c.stringValue = testName1;
+ a.strongTestObjectValue = c;
+
+ expect(a.strongTestObjectValue.stringValue).to.equal(testName1);
+ expect(b.strongTestObjectValue.stringValue).to.equal(testName1);
+ expect(a.strongTestObjectValue).notTo.equal(b.strongTestObjectValue);
+ });
+
+ it(@"should take the value of the object being bound to at the start", ^{
+ a.stringValue = testName1;
+ b.stringValue = testName2;
+
+ RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue);
+ expect(a.stringValue).to.equal(testName2);
+ expect(b.stringValue).to.equal(testName2);
+ });
+
+ it(@"should update the value even if it's the same value the object had before it was bound", ^{
+ a.stringValue = testName1;
+ b.stringValue = testName2;
+
+ RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue);
+ expect(a.stringValue).to.equal(testName2);
+ expect(b.stringValue).to.equal(testName2);
+
+ b.stringValue = testName1;
+ expect(a.stringValue).to.equal(testName1);
+ expect(b.stringValue).to.equal(testName1);
+ });
+
+ it(@"should bind transitively", ^{
+ a.stringValue = testName1;
+ b.stringValue = testName2;
+ c.stringValue = testName3;
+
+ RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue);
+ RACChannelTo(b, stringValue) = RACChannelTo(c, stringValue);
+ expect(a.stringValue).to.equal(testName3);
+ expect(b.stringValue).to.equal(testName3);
+ expect(c.stringValue).to.equal(testName3);
+
+ c.stringValue = testName1;
+ expect(a.stringValue).to.equal(testName1);
+ expect(b.stringValue).to.equal(testName1);
+ expect(c.stringValue).to.equal(testName1);
+
+ b.stringValue = testName2;
+ expect(a.stringValue).to.equal(testName2);
+ expect(b.stringValue).to.equal(testName2);
+ expect(c.stringValue).to.equal(testName2);
+
+ a.stringValue = testName3;
+ expect(a.stringValue).to.equal(testName3);
+ expect(b.stringValue).to.equal(testName3);
+ expect(c.stringValue).to.equal(testName3);
+ });
+
+ it(@"should bind changes made by KVC on arrays", ^{
+ b.arrayValue = @[];
+ RACChannelTo(a, arrayValue) = RACChannelTo(b, arrayValue);
+
+ [[b mutableArrayValueForKeyPath:@keypath(b.arrayValue)] addObject:@1];
+ expect(a.arrayValue).to.equal(b.arrayValue);
+ });
+
+ it(@"should bind changes made by KVC on sets", ^{
+ b.setValue = [NSSet set];
+ RACChannelTo(a, setValue) = RACChannelTo(b, setValue);
+
+ [[b mutableSetValueForKeyPath:@keypath(b.setValue)] addObject:@1];
+ expect(a.setValue).to.equal(b.setValue);
+ });
+
+ it(@"should bind changes made by KVC on ordered sets", ^{
+ b.orderedSetValue = [NSOrderedSet orderedSet];
+ RACChannelTo(a, orderedSetValue) = RACChannelTo(b, orderedSetValue);
+
+ [[b mutableOrderedSetValueForKeyPath:@keypath(b.orderedSetValue)] addObject:@1];
+ expect(a.orderedSetValue).to.equal(b.orderedSetValue);
+ });
+
+ it(@"should handle deallocation of intermediate objects correctly even without support from KVO", ^{
+ __block BOOL wasDisposed = NO;
+
+ RACChannelTo(a, weakTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue);
+ b.strongTestObjectValue = [[RACTestObject alloc] init];
+
+ @autoreleasepool {
+ RACTestObject *object = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ wasDisposed = YES;
+ }]];
+
+ a.weakTestObjectValue = object;
+ object.stringValue = testName1;
+
+ expect(wasDisposed).to.beFalsy();
+ expect(b.strongTestObjectValue.stringValue).to.equal(testName1);
+ }
+
+ expect(wasDisposed).will.beTruthy();
+ expect(b.strongTestObjectValue.stringValue).to.beNil();
+ });
+
+ it(@"should stop binding when disposed", ^{
+ RACChannelTerminal *aTerminal = RACChannelTo(a, stringValue);
+ RACChannelTerminal *bTerminal = RACChannelTo(b, stringValue);
+
+ a.stringValue = testName1;
+ RACDisposable *disposable = [aTerminal subscribe:bTerminal];
+
+ expect(a.stringValue).to.equal(testName1);
+ expect(b.stringValue).to.equal(testName1);
+
+ a.stringValue = testName2;
+ expect(a.stringValue).to.equal(testName2);
+ expect(b.stringValue).to.equal(testName2);
+
+ [disposable dispose];
+
+ a.stringValue = testName3;
+ expect(a.stringValue).to.equal(testName3);
+ expect(b.stringValue).to.equal(testName2);
+ });
+
+ it(@"should use the nilValue when sent nil", ^{
+ RACChannelTerminal *terminal = RACChannelTo(a, integerValue, @5);
+ expect(a.integerValue).to.equal(0);
+
+ [terminal sendNext:@2];
+ expect(a.integerValue).to.equal(2);
+
+ [terminal sendNext:nil];
+ expect(a.integerValue).to.equal(5);
+ });
+
+ it(@"should use the nilValue when an intermediate object is nil", ^{
+ __block BOOL wasDisposed = NO;
+
+ RACChannelTo(a, weakTestObjectValue.integerValue, @5) = RACChannelTo(b, strongTestObjectValue.integerValue, @5);
+ b.strongTestObjectValue = [[RACTestObject alloc] init];
+
+ @autoreleasepool {
+ RACTestObject *object = [[RACTestObject alloc] init];
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ wasDisposed = YES;
+ }]];
+
+ a.weakTestObjectValue = object;
+ object.integerValue = 2;
+
+ expect(wasDisposed).to.beFalsy();
+ expect(b.strongTestObjectValue.integerValue).to.equal(2);
+ }
+
+ expect(wasDisposed).will.beTruthy();
+ expect(b.strongTestObjectValue.integerValue).to.equal(5);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOWrapperSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOWrapperSpec.m
new file mode 100644
index 0000000..dc207ef
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACKVOWrapperSpec.m
@@ -0,0 +1,656 @@
+//
+// RACKVOWrapperSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-08-07.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "NSObject+RACKVOWrapper.h"
+
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACKVOTrampoline.h"
+#import "RACTestObject.h"
+
+@interface RACTestOperation : NSOperation
+@end
+
+// The name of the examples.
+static NSString * const RACKVOWrapperExamples = @"RACKVOWrapperExamples";
+
+// A block that returns an object to observe in the examples.
+static NSString * const RACKVOWrapperExamplesTargetBlock = @"RACKVOWrapperExamplesTargetBlock";
+
+// The key path to observe in the examples.
+//
+// The key path must have at least one weak property in it.
+static NSString * const RACKVOWrapperExamplesKeyPath = @"RACKVOWrapperExamplesKeyPath";
+
+// A block that changes the value of a weak property in the observed key path.
+// The block is passed the object the example is observing and the new value the
+// weak property should be changed to.
+static NSString * const RACKVOWrapperExamplesChangeBlock = @"RACKVOWrapperExamplesChangeBlock";
+
+// A block that returns a valid value for the weak property changed by
+// RACKVOWrapperExamplesChangeBlock. The value must deallocate
+// normally.
+static NSString * const RACKVOWrapperExamplesValueBlock = @"RACKVOWrapperExamplesValueBlock";
+
+// Whether RACKVOWrapperExamplesChangeBlock changes the value
+// of the last key path component in the key path directly.
+static NSString * const RACKVOWrapperExamplesChangesValueDirectly = @"RACKVOWrapperExamplesChangesValueDirectly";
+
+// The name of the examples.
+static NSString * const RACKVOWrapperCollectionExamples = @"RACKVOWrapperCollectionExamples";
+
+// A block that returns an object to observe in the examples.
+static NSString * const RACKVOWrapperCollectionExamplesTargetBlock = @"RACKVOWrapperCollectionExamplesTargetBlock";
+
+// The key path to observe in the examples.
+//
+// Must identify a property of type NSOrderedSet.
+static NSString * const RACKVOWrapperCollectionExamplesKeyPath = @"RACKVOWrapperCollectionExamplesKeyPath";
+
+SharedExampleGroupsBegin(RACKVOWrapperExamples)
+
+sharedExamplesFor(RACKVOWrapperExamples, ^(NSDictionary *data) {
+ __block NSObject *target = nil;
+ __block NSString *keyPath = nil;
+ __block void (^changeBlock)(NSObject *, id) = nil;
+ __block id (^valueBlock)(void) = nil;
+ __block BOOL changesValueDirectly = NO;
+
+ __block NSUInteger priorCallCount = 0;
+ __block NSUInteger posteriorCallCount = 0;
+ __block BOOL priorTriggeredByLastKeyPathComponent = NO;
+ __block BOOL posteriorTriggeredByLastKeyPathComponent = NO;
+ __block BOOL posteriorTriggeredByDeallocation = NO;
+ __block void (^callbackBlock)(id, NSDictionary *) = nil;
+
+ beforeEach(^{
+ NSObject * (^targetBlock)(void) = data[RACKVOWrapperExamplesTargetBlock];
+ target = targetBlock();
+ keyPath = data[RACKVOWrapperExamplesKeyPath];
+ changeBlock = data[RACKVOWrapperExamplesChangeBlock];
+ valueBlock = data[RACKVOWrapperExamplesValueBlock];
+ changesValueDirectly = [data[RACKVOWrapperExamplesChangesValueDirectly] boolValue];
+
+ priorCallCount = 0;
+ posteriorCallCount = 0;
+
+ callbackBlock = [^(id value, NSDictionary *change) {
+ if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
+ priorTriggeredByLastKeyPathComponent = [change[RACKeyValueChangeAffectedOnlyLastComponentKey] boolValue];
+ ++priorCallCount;
+ return;
+ }
+ posteriorTriggeredByLastKeyPathComponent = [change[RACKeyValueChangeAffectedOnlyLastComponentKey] boolValue];
+ posteriorTriggeredByDeallocation = [change[RACKeyValueChangeCausedByDeallocationKey] boolValue];
+ ++posteriorCallCount;
+ } copy];
+ });
+
+ afterEach(^{
+ target = nil;
+ keyPath = nil;
+ changeBlock = nil;
+ valueBlock = nil;
+ changesValueDirectly = NO;
+
+ callbackBlock = nil;
+ });
+
+ it(@"should not call the callback block on add if called without NSKeyValueObservingOptionInitial", ^{
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock];
+ expect(priorCallCount).to.equal(0);
+ expect(posteriorCallCount).to.equal(0);
+ });
+
+ it(@"should call the callback block on add if called with NSKeyValueObservingOptionInitial", ^{
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionInitial observer:nil block:callbackBlock];
+ expect(priorCallCount).to.equal(0);
+ expect(posteriorCallCount).to.equal(1);
+ });
+
+ it(@"should call the callback block twice per change, once prior and once posterior", ^{
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock];
+ priorCallCount = 0;
+ posteriorCallCount = 0;
+
+ id value1 = valueBlock();
+ changeBlock(target, value1);
+ expect(priorCallCount).to.equal(1);
+ expect(posteriorCallCount).to.equal(1);
+ expect(priorTriggeredByLastKeyPathComponent).to.equal(changesValueDirectly);
+ expect(posteriorTriggeredByLastKeyPathComponent).to.equal(changesValueDirectly);
+ expect(posteriorTriggeredByDeallocation).to.beFalsy();
+
+ id value2 = valueBlock();
+ changeBlock(target, value2);
+ expect(priorCallCount).to.equal(2);
+ expect(posteriorCallCount).to.equal(2);
+ expect(priorTriggeredByLastKeyPathComponent).to.equal(changesValueDirectly);
+ expect(posteriorTriggeredByLastKeyPathComponent).to.equal(changesValueDirectly);
+ expect(posteriorTriggeredByDeallocation).to.beFalsy();
+ });
+
+ it(@"should call the callback block with NSKeyValueChangeNotificationIsPriorKey set before the value is changed, and not set after the value is changed", ^{
+ __block BOOL priorCalled = NO;
+ __block BOOL posteriorCalled = NO;
+ __block id priorValue = nil;
+ __block id posteriorValue = nil;
+
+ id value1 = valueBlock();
+ changeBlock(target, value1);
+ id oldValue = [target valueForKeyPath:keyPath];
+
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:^(id value, NSDictionary *change) {
+ if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
+ priorCalled = YES;
+ priorValue = value;
+ expect(posteriorCalled).to.beFalsy();
+ return;
+ }
+ posteriorCalled = YES;
+ posteriorValue = value;
+ expect(priorCalled).to.beTruthy();
+ }];
+
+ id value2 = valueBlock();
+ changeBlock(target, value2);
+ id newValue = [target valueForKeyPath:keyPath];
+ expect(priorCalled).to.beTruthy();
+ expect(priorValue).to.equal(oldValue);
+ expect(posteriorCalled).to.beTruthy();
+ expect(posteriorValue).to.equal(newValue);
+ });
+
+ it(@"should not call the callback block after it's been disposed", ^{
+ RACDisposable *disposable = [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock];
+ priorCallCount = 0;
+ posteriorCallCount = 0;
+
+ [disposable dispose];
+ expect(priorCallCount).to.equal(0);
+ expect(posteriorCallCount).to.equal(0);
+
+ id value = valueBlock();
+ changeBlock(target, value);
+ expect(priorCallCount).to.equal(0);
+ expect(posteriorCallCount).to.equal(0);
+ });
+
+ it(@"should call the callback block only once with NSKeyValueChangeNotificationIsPriorKey not set when the value is deallocated", ^{
+ __block BOOL valueDidDealloc = NO;
+
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock];
+
+ @autoreleasepool {
+ NSObject *value __attribute__((objc_precise_lifetime)) = valueBlock();
+ [value.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ valueDidDealloc = YES;
+ }]];
+
+ changeBlock(target, value);
+ priorCallCount = 0;
+ posteriorCallCount = 0;
+ }
+
+ expect(valueDidDealloc).to.beTruthy();
+ expect(priorCallCount).to.equal(0);
+ expect(posteriorCallCount).to.equal(1);
+ expect(posteriorTriggeredByDeallocation).to.beTruthy();
+ });
+});
+
+sharedExamplesFor(RACKVOWrapperCollectionExamples, ^(NSDictionary *data) {
+ __block NSObject *target = nil;
+ __block NSString *keyPath = nil;
+ __block NSMutableOrderedSet *mutableKeyPathProxy = nil;
+ __block void (^callbackBlock)(id, NSDictionary *) = nil;
+
+ __block id priorValue = nil;
+ __block id posteriorValue = nil;
+ __block NSDictionary *priorChange = nil;
+ __block NSDictionary *posteriorChange = nil;
+
+ beforeEach(^{
+ NSObject * (^targetBlock)(void) = data[RACKVOWrapperCollectionExamplesTargetBlock];
+ target = targetBlock();
+ keyPath = data[RACKVOWrapperCollectionExamplesKeyPath];
+
+ callbackBlock = [^(id value, NSDictionary *change) {
+ if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
+ priorValue = value;
+ priorChange = change;
+ return;
+ }
+ posteriorValue = value;
+ posteriorChange = change;
+ } copy];
+
+ [target setValue:[NSOrderedSet orderedSetWithObject:@0] forKeyPath:keyPath];
+ [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior observer:nil block:callbackBlock];
+ mutableKeyPathProxy = [target mutableOrderedSetValueForKeyPath:keyPath];
+ });
+
+ afterEach(^{
+ target = nil;
+ keyPath = nil;
+ callbackBlock = nil;
+
+ priorValue = nil;
+ priorChange = nil;
+ posteriorValue = nil;
+ posteriorChange = nil;
+ });
+
+ it(@"should support inserting elements into ordered collections", ^{
+ [mutableKeyPathProxy insertObject:@1 atIndex:0];
+
+ expect(priorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @0 ]]);
+ expect(posteriorValue).to.equal([NSOrderedSet orderedSetWithArray:(@[ @1, @0 ])]);
+ expect(priorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeInsertion);
+ expect(posteriorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeInsertion);
+ expect(priorChange[NSKeyValueChangeOldKey]).to.beNil();
+ expect(posteriorChange[NSKeyValueChangeNewKey]).to.equal(@[ @1 ]);
+ expect(priorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ expect(posteriorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ });
+
+ it(@"should support removing elements from ordered collections", ^{
+ [mutableKeyPathProxy removeObjectAtIndex:0];
+
+ expect(priorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @0 ]]);
+ expect(posteriorValue).to.equal([NSOrderedSet orderedSetWithArray:@[]]);
+ expect(priorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeRemoval);
+ expect(posteriorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeRemoval);
+ expect(priorChange[NSKeyValueChangeOldKey]).to.equal(@[ @0 ]);
+ expect(posteriorChange[NSKeyValueChangeNewKey]).to.beNil();
+ expect(priorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ expect(posteriorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ });
+
+ it(@"should support replacing elements in ordered collections", ^{
+ [mutableKeyPathProxy replaceObjectAtIndex:0 withObject:@1];
+
+ expect(priorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @0 ]]);
+ expect(posteriorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @1 ]]);
+ expect(priorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeReplacement);
+ expect(posteriorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeReplacement);
+ expect(priorChange[NSKeyValueChangeOldKey]).to.equal(@[ @0 ]);
+ expect(posteriorChange[NSKeyValueChangeNewKey]).to.equal(@[ @1 ]);
+ expect(priorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ expect(posteriorChange[NSKeyValueChangeIndexesKey]).to.equal([NSIndexSet indexSetWithIndex:0]);
+ });
+
+ it(@"should support adding elements to unordered collections", ^{
+ [mutableKeyPathProxy unionOrderedSet:[NSOrderedSet orderedSetWithObject:@1]];
+
+ expect(priorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @0 ]]);
+ expect(posteriorValue).to.equal([NSOrderedSet orderedSetWithArray:(@[ @0, @1 ])]);
+ expect(priorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeInsertion);
+ expect(posteriorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeInsertion);
+ expect(priorChange[NSKeyValueChangeOldKey]).to.beNil();
+ expect(posteriorChange[NSKeyValueChangeNewKey]).to.equal(@[ @1 ]);
+ });
+
+ it(@"should support removing elements from unordered collections", ^{
+ [mutableKeyPathProxy minusOrderedSet:[NSOrderedSet orderedSetWithObject:@0]];
+
+ expect(priorValue).to.equal([NSOrderedSet orderedSetWithArray:@[ @0 ]]);
+ expect(posteriorValue).to.equal([NSOrderedSet orderedSetWithArray:@[]]);
+ expect(priorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeRemoval);
+ expect(posteriorChange[NSKeyValueChangeKindKey]).to.equal(NSKeyValueChangeRemoval);
+ expect(priorChange[NSKeyValueChangeOldKey]).to.equal(@[ @0 ]);
+ expect(posteriorChange[NSKeyValueChangeNewKey]).to.beNil();
+ });
+});
+
+SharedExampleGroupsEnd
+
+SpecBegin(RACKVOWrapper)
+
+describe(@"-rac_observeKeyPath:options:observer:block:", ^{
+ describe(@"on simple keys", ^{
+ NSObject * (^targetBlock)(void) = ^{
+ return [[RACTestObject alloc] init];
+ };
+
+ void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) {
+ target.weakTestObjectValue = value;
+ };
+
+ id (^valueBlock)(void) = ^{
+ return [[RACTestObject alloc] init];
+ };
+
+ itShouldBehaveLike(RACKVOWrapperExamples, @{
+ RACKVOWrapperExamplesTargetBlock: targetBlock,
+ RACKVOWrapperExamplesKeyPath: @keypath(RACTestObject.new, weakTestObjectValue),
+ RACKVOWrapperExamplesChangeBlock: changeBlock,
+ RACKVOWrapperExamplesValueBlock: valueBlock,
+ RACKVOWrapperExamplesChangesValueDirectly: @YES
+ });
+
+ itShouldBehaveLike(RACKVOWrapperCollectionExamples, @{
+ RACKVOWrapperCollectionExamplesTargetBlock: targetBlock,
+ RACKVOWrapperCollectionExamplesKeyPath: @keypath(RACTestObject.new, orderedSetValue)
+ });
+ });
+
+ describe(@"on composite key paths'", ^{
+ describe(@"last key path components", ^{
+ NSObject *(^targetBlock)(void) = ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ object.strongTestObjectValue = [[RACTestObject alloc] init];
+ return object;
+ };
+
+ void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) {
+ target.strongTestObjectValue.weakTestObjectValue = value;
+ };
+
+ id (^valueBlock)(void) = ^{
+ return [[RACTestObject alloc] init];
+ };
+
+ itShouldBehaveLike(RACKVOWrapperExamples, @{
+ RACKVOWrapperExamplesTargetBlock: targetBlock,
+ RACKVOWrapperExamplesKeyPath: @keypath(RACTestObject.new, strongTestObjectValue.weakTestObjectValue),
+ RACKVOWrapperExamplesChangeBlock: changeBlock,
+ RACKVOWrapperExamplesValueBlock: valueBlock,
+ RACKVOWrapperExamplesChangesValueDirectly: @YES
+ });
+
+ itShouldBehaveLike(RACKVOWrapperCollectionExamples, @{
+ RACKVOWrapperCollectionExamplesTargetBlock: targetBlock,
+ RACKVOWrapperCollectionExamplesKeyPath: @keypath(RACTestObject.new, strongTestObjectValue.orderedSetValue)
+ });
+ });
+
+ describe(@"intermediate key path components", ^{
+ NSObject *(^targetBlock)(void) = ^{
+ return [[RACTestObject alloc] init];
+ };
+
+ void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) {
+ target.weakTestObjectValue = value;
+ };
+
+ id (^valueBlock)(void) = ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+ object.strongTestObjectValue = [[RACTestObject alloc] init];
+ return object;
+ };
+
+ itShouldBehaveLike(RACKVOWrapperExamples, @{
+ RACKVOWrapperExamplesTargetBlock: targetBlock,
+ RACKVOWrapperExamplesKeyPath: @keypath([[RACTestObject alloc] init], weakTestObjectValue.strongTestObjectValue),
+ RACKVOWrapperExamplesChangeBlock: changeBlock,
+ RACKVOWrapperExamplesValueBlock: valueBlock,
+ RACKVOWrapperExamplesChangesValueDirectly: @NO
+ });
+ });
+
+ it(@"should not notice deallocation of the object returned by a dynamic final property", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id lastValue = nil;
+ @autoreleasepool {
+ [object rac_observeKeyPath:@keypath(object.dynamicObjectProperty) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change) {
+ lastValue = value;
+ }];
+
+ expect(lastValue).to.beKindOf(RACTestObject.class);
+ }
+
+ expect(lastValue).to.beKindOf(RACTestObject.class);
+ });
+
+ it(@"should not notice deallocation of the object returned by a dynamic intermediate property", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id lastValue = nil;
+ @autoreleasepool {
+ [object rac_observeKeyPath:@keypath(object.dynamicObjectProperty.integerValue) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change) {
+ lastValue = value;
+ }];
+
+ expect(lastValue).to.equal(@42);
+ }
+
+ expect(lastValue).to.equal(@42);
+ });
+
+ it(@"should not notice deallocation of the object returned by a dynamic method", ^{
+ RACTestObject *object = [[RACTestObject alloc] init];
+
+ __block id lastValue = nil;
+ @autoreleasepool {
+ [object rac_observeKeyPath:@keypath(object.dynamicObjectMethod) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change) {
+ lastValue = value;
+ }];
+
+ expect(lastValue).to.beKindOf(RACTestObject.class);
+ }
+
+ expect(lastValue).to.beKindOf(RACTestObject.class);
+ });
+ });
+
+ it(@"should not call the callback block when the value is the observer", ^{
+ __block BOOL observerDisposed = NO;
+ __block BOOL observerDeallocationTriggeredChange = NO;
+ __block BOOL targetDisposed = NO;
+ __block BOOL targetDeallocationTriggeredChange = NO;
+
+ @autoreleasepool {
+ RACTestObject *observer __attribute__((objc_precise_lifetime)) = [RACTestObject new];
+ [observer.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ observerDisposed = YES;
+ }]];
+
+ RACTestObject *target __attribute__((objc_precise_lifetime)) = [RACTestObject new];
+ [target.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ targetDisposed = YES;
+ }]];
+
+ observer.weakTestObjectValue = observer;
+ target.weakTestObjectValue = target;
+
+ // These observations can only result in dealloc triggered callbacks.
+ [observer rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:observer block:^(id _, NSDictionary *__) {
+ observerDeallocationTriggeredChange = YES;
+ }];
+
+ [target rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:observer block:^(id _, NSDictionary *__) {
+ targetDeallocationTriggeredChange = YES;
+ }];
+ }
+
+ expect(observerDisposed).to.beTruthy();
+ expect(observerDeallocationTriggeredChange).to.beFalsy();
+
+ expect(targetDisposed).to.beTruthy();
+ expect(targetDeallocationTriggeredChange).to.beTruthy();
+ });
+
+ it(@"should call the callback block for deallocation of the initial value of a single-key key path", ^{
+ RACTestObject *target = [RACTestObject new];
+ __block BOOL objectDisposed = NO;
+ __block BOOL objectDeallocationTriggeredChange = NO;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [RACTestObject new];
+ target.weakTestObjectValue = object;
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ objectDisposed = YES;
+ }]];
+
+ [target rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:target block:^(id _, NSDictionary *__) {
+ objectDeallocationTriggeredChange = YES;
+ }];
+ }
+
+ expect(objectDisposed).to.beTruthy();
+ expect(objectDeallocationTriggeredChange).to.beTruthy();
+ });
+
+ it(@"should call the callback block for deallocation of an object conforming to protocol property", ^{
+ RACTestObject *target = [RACTestObject new];
+ __block BOOL objectDisposed = NO;
+ __block BOOL objectDeallocationTriggeredChange = NO;
+
+ @autoreleasepool {
+ RACTestObject *object __attribute__((objc_precise_lifetime)) = [RACTestObject new];
+ target.weakObjectWithProtocol = object;
+ [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ objectDisposed = YES;
+ }]];
+
+ [target rac_observeKeyPath:@keypath(target.weakObjectWithProtocol) options:0 observer:target block:^(id _, NSDictionary *__) {
+ objectDeallocationTriggeredChange = YES;
+ }];
+ }
+
+ expect(objectDisposed).to.beTruthy();
+ expect(objectDeallocationTriggeredChange).to.beTruthy();
+ });
+});
+
+describe(@"rac_addObserver:forKeyPath:options:block:", ^{
+ it(@"should add and remove an observer", ^{
+ NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{}];
+ expect(operation).notTo.beNil();
+
+ __block BOOL notified = NO;
+ RACDisposable *disposable = [operation rac_observeKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew observer:self block:^(id value, NSDictionary *change) {
+ expect([change objectForKey:NSKeyValueChangeNewKey]).to.equal(@YES);
+
+ expect(notified).to.beFalsy();
+ notified = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+
+ [operation start];
+ [operation waitUntilFinished];
+
+ expect(notified).will.beTruthy();
+ });
+
+ it(@"should accept a nil observer", ^{
+ NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{}];
+ RACDisposable *disposable = [operation rac_observeKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change) {}];
+
+ expect(disposable).notTo.beNil();
+ });
+
+ it(@"automatically stops KVO on subclasses when the target deallocates", ^{
+ void (^testKVOOnSubclass)(Class targetClass, id observer) = ^(Class targetClass, id observer) {
+ __weak id weakTarget = nil;
+ __weak id identifier = nil;
+
+ @autoreleasepool {
+ // Create an observable target that we control the memory management of.
+ CFTypeRef target = CFBridgingRetain([[targetClass alloc] init]);
+ expect(target).notTo.beNil();
+
+ weakTarget = (__bridge id)target;
+ expect(weakTarget).notTo.beNil();
+
+ identifier = [(__bridge id)target rac_observeKeyPath:@"isFinished" options:0 observer:observer block:^(id value, NSDictionary *change) {}];
+ expect(identifier).notTo.beNil();
+
+ CFRelease(target);
+ }
+
+ expect(weakTarget).to.beNil();
+ expect(identifier).to.beNil();
+ };
+
+ it (@"stops KVO on NSObject subclasses", ^{
+ testKVOOnSubclass(NSOperation.class, self);
+ });
+
+ it(@"stops KVO on subclasses of already-swizzled classes", ^{
+ testKVOOnSubclass(RACTestOperation.class, self);
+ });
+
+ it (@"stops KVO on NSObject subclasses even with a nil observer", ^{
+ testKVOOnSubclass(NSOperation.class, nil);
+ });
+
+ it(@"stops KVO on subclasses of already-swizzled classes even with a nil observer", ^{
+ testKVOOnSubclass(RACTestOperation.class, nil);
+ });
+ });
+
+ it(@"should automatically stop KVO when the observer deallocates", ^{
+ __weak id weakObserver = nil;
+ __weak id identifier = nil;
+
+ NSOperation *operation = [[NSOperation alloc] init];
+
+ @autoreleasepool {
+ // Create an observer that we control the memory management of.
+ CFTypeRef observer = CFBridgingRetain([[NSOperation alloc] init]);
+ expect(observer).notTo.beNil();
+
+ weakObserver = (__bridge id)observer;
+ expect(weakObserver).notTo.beNil();
+
+ identifier = [operation rac_observeKeyPath:@"isFinished" options:0 observer:(__bridge id)observer block:^(id value, NSDictionary *change) {}];
+ expect(identifier).notTo.beNil();
+
+ CFRelease(observer);
+ }
+
+ expect(weakObserver).to.beNil();
+ });
+
+ it(@"should stop KVO when the observer is disposed", ^{
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ __block NSString *name = nil;
+
+ RACDisposable *disposable = [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change) {
+ name = queue.name;
+ }];
+
+ queue.name = @"1";
+ expect(name).to.equal(@"1");
+ [disposable dispose];
+ queue.name = @"2";
+ expect(name).to.equal(@"1");
+ });
+
+ it(@"should distinguish between observers being disposed", ^{
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ __block NSString *name1 = nil;
+ __block NSString *name2 = nil;
+
+ RACDisposable *disposable = [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change) {
+ name1 = queue.name;
+ }];
+ [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change) {
+ name2 = queue.name;
+ }];
+
+ queue.name = @"1";
+ expect(name1).to.equal(@"1");
+ expect(name2).to.equal(@"1");
+ [disposable dispose];
+ queue.name = @"2";
+ expect(name1).to.equal(@"1");
+ expect(name2).to.equal(@"2");
+ });
+});
+
+SpecEnd
+
+@implementation RACTestOperation
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACMulticastConnectionSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACMulticastConnectionSpec.m
new file mode 100644
index 0000000..b043beb
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACMulticastConnectionSpec.m
@@ -0,0 +1,142 @@
+//
+// RACMulticastConnectionSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 10/8/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACMulticastConnection.h"
+#import "RACDisposable.h"
+#import "RACSignal+Operations.h"
+#import "RACSubscriber.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import <libkern/OSAtomic.h>
+
+SpecBegin(RACMulticastConnection)
+
+__block NSUInteger subscriptionCount = 0;
+__block RACMulticastConnection *connection;
+__block BOOL disposed = NO;
+
+beforeEach(^{
+ subscriptionCount = 0;
+ disposed = NO;
+ connection = [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ subscriptionCount++;
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }] publish];
+ expect(subscriptionCount).to.equal(0);
+});
+
+describe(@"-connect", ^{
+ it(@"should subscribe to the underlying signal", ^{
+ [connection connect];
+ expect(subscriptionCount).to.equal(1);
+ });
+
+ it(@"should return the same disposable for each invocation", ^{
+ RACDisposable *d1 = [connection connect];
+ RACDisposable *d2 = [connection connect];
+ expect(d1).to.equal(d2);
+ expect(subscriptionCount).to.equal(1);
+ });
+
+ it(@"shouldn't reconnect after disposal", ^{
+ RACDisposable *disposable1 = [connection connect];
+ expect(subscriptionCount).to.equal(1);
+
+ [disposable1 dispose];
+
+ RACDisposable *disposable2 = [connection connect];
+ expect(subscriptionCount).to.equal(1);
+ expect(disposable1).to.equal(disposable2);
+ });
+
+ it(@"shouldn't race when connecting", ^{
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+
+ RACMulticastConnection *connection = [[RACSignal
+ defer:^ id {
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+ return nil;
+ }]
+ publish];
+
+ __block RACDisposable *disposable;
+ [RACScheduler.scheduler schedule:^{
+ disposable = [connection connect];
+ dispatch_semaphore_signal(semaphore);
+ }];
+
+ expect([connection connect]).notTo.beNil();
+ dispatch_semaphore_signal(semaphore);
+
+ expect(disposable).willNot.beNil();
+
+ dispatch_release(semaphore);
+ });
+});
+
+describe(@"-autoconnect", ^{
+ __block RACSignal *autoconnectedSignal;
+
+ beforeEach(^{
+ autoconnectedSignal = [connection autoconnect];
+ });
+
+ it(@"should subscribe to the multicasted signal on the first subscription", ^{
+ expect(subscriptionCount).to.equal(0);
+
+ [autoconnectedSignal subscribeNext:^(id x) {}];
+ expect(subscriptionCount).to.equal(1);
+
+ [autoconnectedSignal subscribeNext:^(id x) {}];
+ expect(subscriptionCount).to.equal(1);
+ });
+
+ it(@"should dispose of the multicasted subscription when the signal has no subscribers", ^{
+ RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}];
+
+ expect(disposed).to.beFalsy();
+ [disposable dispose];
+ expect(disposed).to.beTruthy();
+ });
+
+ it(@"shouldn't reconnect after disposal", ^{
+ RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}];
+ expect(subscriptionCount).to.equal(1);
+ [disposable dispose];
+
+ disposable = [autoconnectedSignal subscribeNext:^(id x) {}];
+ expect(subscriptionCount).to.equal(1);
+ [disposable dispose];
+ });
+
+ it(@"should replay values after disposal when multicasted to a replay subject", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSignal *signal = [[subject multicast:[RACReplaySubject subject]] autoconnect];
+
+ NSMutableArray *results1 = [NSMutableArray array];
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {
+ [results1 addObject:x];
+ }];
+
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+
+ expect(results1).to.equal((@[ @1, @2 ]));
+ [disposable dispose];
+
+ NSMutableArray *results2 = [NSMutableArray array];
+ [signal subscribeNext:^(id x) {
+ [results2 addObject:x];
+ }];
+ expect(results2).will.equal((@[ @1, @2 ]));
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.h
new file mode 100644
index 0000000..59a2b43
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.h
@@ -0,0 +1,18 @@
+//
+// RACPropertySignalExamples.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for a signal-driven property.
+extern NSString * const RACPropertySignalExamples;
+
+// The block should have the signature:
+//
+// void (^)(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal)
+//
+// and should tie the value of the key path on testObject to signal. `nilValue`
+// will be used when the signal sends a `nil` value.
+extern NSString * const RACPropertySignalExamplesSetupBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.m
new file mode 100644
index 0000000..627d634
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.m
@@ -0,0 +1,180 @@
+//
+// RACPropertySignalExamples.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/28/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSubject.h"
+
+NSString * const RACPropertySignalExamples = @"RACPropertySignalExamples";
+NSString * const RACPropertySignalExamplesSetupBlock = @"RACPropertySignalExamplesSetupBlock";
+
+SharedExampleGroupsBegin(RACPropertySignalExamples)
+
+sharedExamplesFor(RACPropertySignalExamples, ^(NSDictionary *data) {
+ __block RACTestObject *testObject = nil;
+ __block void (^setupBlock)(RACTestObject *, NSString *keyPath, id nilValue, RACSignal *);
+
+ beforeEach(^{
+ setupBlock = data[RACPropertySignalExamplesSetupBlock];
+ testObject = [[RACTestObject alloc] init];
+ });
+
+ it(@"should set the value of the property with the latest value from the signal", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.objectValue), nil, subject);
+ expect(testObject.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(testObject.objectValue).to.equal(@1);
+
+ [subject sendNext:@2];
+ expect(testObject.objectValue).to.equal(@2);
+
+ [subject sendNext:nil];
+ expect(testObject.objectValue).to.beNil();
+ });
+
+ it(@"should set the given nilValue for an object property", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.objectValue), @"foo", subject);
+ expect(testObject.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(testObject.objectValue).to.equal(@1);
+
+ [subject sendNext:@2];
+ expect(testObject.objectValue).to.equal(@2);
+
+ [subject sendNext:nil];
+ expect(testObject.objectValue).to.equal(@"foo");
+ });
+
+ it(@"should leave the value of the property alone after the signal completes", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.objectValue), nil, subject);
+ expect(testObject.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(testObject.objectValue).to.equal(@1);
+
+ [subject sendCompleted];
+ expect(testObject.objectValue).to.equal(@1);
+ });
+
+ it(@"should set the value of a non-object property with the latest value from the signal", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.integerValue), nil, subject);
+ expect(testObject.integerValue).to.equal(0);
+
+ [subject sendNext:@1];
+ expect(testObject.integerValue).to.equal(1);
+
+ [subject sendNext:@2];
+ expect(testObject.integerValue).to.equal(2);
+
+ [subject sendNext:@0];
+ expect(testObject.integerValue).to.equal(0);
+ });
+
+ it(@"should set the given nilValue for a non-object property", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.integerValue), @42, subject);
+ expect(testObject.integerValue).to.equal(0);
+
+ [subject sendNext:@1];
+ expect(testObject.integerValue).to.equal(@1);
+
+ [subject sendNext:@2];
+ expect(testObject.integerValue).to.equal(@2);
+
+ [subject sendNext:nil];
+ expect(testObject.integerValue).to.equal(@42);
+ });
+
+ it(@"should not invoke -setNilValueForKey: with a nilValue", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.integerValue), @42, subject);
+
+ __block BOOL setNilValueForKeyInvoked = NO;
+ [[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) {
+ setNilValueForKeyInvoked = YES;
+ }];
+
+ [subject sendNext:nil];
+ expect(testObject.integerValue).to.equal(@42);
+ expect(setNilValueForKeyInvoked).to.beFalsy();
+ });
+
+ it(@"should invoke -setNilValueForKey: without a nilValue", ^{
+ RACSubject *subject = [RACSubject subject];
+ setupBlock(testObject, @keypath(testObject.integerValue), nil, subject);
+
+ [subject sendNext:@1];
+ expect(testObject.integerValue).to.equal(@1);
+
+ testObject.catchSetNilValueForKey = YES;
+
+ __block BOOL setNilValueForKeyInvoked = NO;
+ [[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) {
+ setNilValueForKeyInvoked = YES;
+ }];
+
+ [subject sendNext:nil];
+ expect(testObject.integerValue).to.equal(@1);
+ expect(setNilValueForKeyInvoked).to.beTruthy();
+ });
+
+ it(@"should retain intermediate signals when binding", ^{
+ RACSubject *subject = [RACSubject subject];
+ expect(subject).notTo.beNil();
+
+ __block BOOL deallocd = NO;
+
+ @autoreleasepool {
+ @autoreleasepool {
+ RACSignal *intermediateSignal = [subject map:^(NSNumber *num) {
+ return @(num.integerValue + 1);
+ }];
+
+ expect(intermediateSignal).notTo.beNil();
+
+ [intermediateSignal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ setupBlock(testObject, @keypath(testObject.integerValue), nil, intermediateSignal);
+ }
+
+ // Spin the run loop to account for RAC magic that retains the
+ // signal for a single iteration.
+ [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
+ }
+
+ expect(deallocd).to.beFalsy();
+
+ [subject sendNext:@5];
+ expect(testObject.integerValue).to.equal(6);
+
+ [subject sendNext:@6];
+ expect(testObject.integerValue).to.equal(7);
+
+ expect(deallocd).to.beFalsy();
+ [subject sendCompleted];
+
+ // Can't test deallocd again, because it's legal for the chain to be
+ // retained until the object or the original signal is destroyed.
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSchedulerSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSchedulerSpec.m
new file mode 100644
index 0000000..738eff8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSchedulerSpec.m
@@ -0,0 +1,424 @@
+//
+// RACSchedulerSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 11/29/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACScheduler.h"
+#import "RACScheduler+Private.h"
+#import "RACQueueScheduler+Subclass.h"
+#import "RACDisposable.h"
+#import "EXTScope.h"
+#import "RACTestExampleScheduler.h"
+#import <libkern/OSAtomic.h>
+
+// This shouldn't be used directly. Use the `expectCurrentSchedulers` block
+// below instead.
+static void expectCurrentSchedulersInner(NSArray *schedulers, NSMutableArray *currentSchedulerArray) {
+ if (schedulers.count > 0) {
+ RACScheduler *topScheduler = schedulers[0];
+ [topScheduler schedule:^{
+ RACScheduler *currentScheduler = RACScheduler.currentScheduler;
+ if (currentScheduler != nil) [currentSchedulerArray addObject:currentScheduler];
+ expectCurrentSchedulersInner([schedulers subarrayWithRange:NSMakeRange(1, schedulers.count - 1)], currentSchedulerArray);
+ }];
+ }
+}
+
+SpecBegin(RACScheduler)
+
+it(@"should know its current scheduler", ^{
+ // Recursively schedules a block in each of the given schedulers and records
+ // the +currentScheduler at each step. It then expects the array of
+ // +currentSchedulers and the expected array to be equal.
+ //
+ // schedulers - The array of schedulers to recursively schedule.
+ // expectedCurrentSchedulers - The array of +currentSchedulers to expect.
+ void (^expectCurrentSchedulers)(NSArray *, NSArray *) = ^(NSArray *schedulers, NSArray *expectedCurrentSchedulers) {
+ NSMutableArray *currentSchedulerArray = [NSMutableArray array];
+ expectCurrentSchedulersInner(schedulers, currentSchedulerArray);
+ expect(currentSchedulerArray).will.equal(expectedCurrentSchedulers);
+ };
+
+ RACScheduler *backgroundScheduler = [RACScheduler scheduler];
+
+ expectCurrentSchedulers(@[ backgroundScheduler, RACScheduler.immediateScheduler ], @[ backgroundScheduler, backgroundScheduler ]);
+ expectCurrentSchedulers(@[ backgroundScheduler, RACScheduler.subscriptionScheduler ], @[ backgroundScheduler, backgroundScheduler ]);
+
+ NSArray *mainThreadJumper = @[ RACScheduler.mainThreadScheduler, backgroundScheduler, RACScheduler.mainThreadScheduler ];
+ expectCurrentSchedulers(mainThreadJumper, mainThreadJumper);
+
+ NSArray *backgroundJumper = @[ backgroundScheduler, RACScheduler.mainThreadScheduler, backgroundScheduler ];
+ expectCurrentSchedulers(backgroundJumper, backgroundJumper);
+});
+
+describe(@"+mainThreadScheduler", ^{
+ it(@"should cancel scheduled blocks when disposed", ^{
+ __block BOOL firstBlockRan = NO;
+ __block BOOL secondBlockRan = NO;
+
+ RACDisposable *disposable = [RACScheduler.mainThreadScheduler schedule:^{
+ firstBlockRan = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+
+ [RACScheduler.mainThreadScheduler schedule:^{
+ secondBlockRan = YES;
+ }];
+
+ [disposable dispose];
+
+ expect(secondBlockRan).to.beFalsy();
+ expect(secondBlockRan).will.beTruthy();
+ expect(firstBlockRan).to.beFalsy();
+ });
+
+ it(@"should schedule future blocks", ^{
+ __block BOOL done = NO;
+
+ [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ done = YES;
+ }];
+
+ expect(done).to.beFalsy();
+ expect(done).will.beTruthy();
+ });
+
+ it(@"should cancel future blocks when disposed", ^{
+ __block BOOL firstBlockRan = NO;
+ __block BOOL secondBlockRan = NO;
+
+ RACDisposable *disposable = [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ firstBlockRan = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+
+ [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ secondBlockRan = YES;
+ }];
+
+ [disposable dispose];
+
+ expect(secondBlockRan).to.beFalsy();
+ expect(secondBlockRan).will.beTruthy();
+ expect(firstBlockRan).to.beFalsy();
+ });
+
+ it(@"should schedule recurring blocks", ^{
+ __block NSUInteger count = 0;
+
+ RACDisposable *disposable = [RACScheduler.mainThreadScheduler after:[NSDate date] repeatingEvery:0.05 withLeeway:0 schedule:^{
+ count++;
+ }];
+
+ expect(count).to.equal(0);
+ expect(count).will.equal(1);
+ expect(count).will.equal(2);
+ expect(count).will.equal(3);
+
+ [disposable dispose];
+ [NSRunLoop.mainRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+
+ expect(count).to.equal(3);
+ });
+});
+
+describe(@"+scheduler", ^{
+ __block RACScheduler *scheduler;
+ __block NSDate * (^futureDate)(void);
+
+ beforeEach(^{
+ scheduler = [RACScheduler scheduler];
+
+ futureDate = ^{
+ return [NSDate dateWithTimeIntervalSinceNow:0.01];
+ };
+ });
+
+ it(@"should cancel scheduled blocks when disposed", ^{
+ __block BOOL firstBlockRan = NO;
+ __block BOOL secondBlockRan = NO;
+
+ // Start off on the scheduler so the enqueued blocks won't run until we
+ // return.
+ [scheduler schedule:^{
+ RACDisposable *disposable = [scheduler schedule:^{
+ firstBlockRan = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+
+ [scheduler schedule:^{
+ secondBlockRan = YES;
+ }];
+
+ [disposable dispose];
+ }];
+
+ expect(secondBlockRan).will.beTruthy();
+ expect(firstBlockRan).to.beFalsy();
+ });
+
+ it(@"should schedule future blocks", ^{
+ __block BOOL done = NO;
+
+ [scheduler after:futureDate() schedule:^{
+ done = YES;
+ }];
+
+ expect(done).to.beFalsy();
+ expect(done).will.beTruthy();
+ });
+
+ it(@"should cancel future blocks when disposed", ^{
+ __block BOOL firstBlockRan = NO;
+ __block BOOL secondBlockRan = NO;
+
+ NSDate *date = futureDate();
+ RACDisposable *disposable = [scheduler after:date schedule:^{
+ firstBlockRan = YES;
+ }];
+
+ expect(disposable).notTo.beNil();
+ [disposable dispose];
+
+ [scheduler after:date schedule:^{
+ secondBlockRan = YES;
+ }];
+
+ expect(secondBlockRan).to.beFalsy();
+ expect(secondBlockRan).will.beTruthy();
+ expect(firstBlockRan).to.beFalsy();
+ });
+
+ it(@"should schedule recurring blocks", ^{
+ __block NSUInteger count = 0;
+
+ RACDisposable *disposable = [scheduler after:[NSDate date] repeatingEvery:0.05 withLeeway:0 schedule:^{
+ count++;
+ }];
+
+ expect(count).to.equal(0);
+ expect(count).will.equal(1);
+ expect(count).will.equal(2);
+ expect(count).will.equal(3);
+
+ [disposable dispose];
+ [NSThread sleepForTimeInterval:0.1];
+
+ expect(count).to.equal(3);
+ });
+});
+
+describe(@"+subscriptionScheduler", ^{
+ describe(@"setting +currentScheduler", ^{
+ __block RACScheduler *currentScheduler;
+
+ beforeEach(^{
+ currentScheduler = nil;
+ });
+
+ it(@"should be the +mainThreadScheduler when scheduled from the main queue", ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [RACScheduler.subscriptionScheduler schedule:^{
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+ });
+
+ expect(currentScheduler).will.equal(RACScheduler.mainThreadScheduler);
+ });
+
+ it(@"should be a +scheduler when scheduled from an unknown queue", ^{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [RACScheduler.subscriptionScheduler schedule:^{
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+ });
+
+ expect(currentScheduler).willNot.beNil();
+ expect(currentScheduler).notTo.equal(RACScheduler.mainThreadScheduler);
+ });
+
+ it(@"should equal the background scheduler from which the block was scheduled", ^{
+ RACScheduler *backgroundScheduler = [RACScheduler scheduler];
+ [backgroundScheduler schedule:^{
+ [RACScheduler.subscriptionScheduler schedule:^{
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+ }];
+
+ expect(currentScheduler).will.equal(backgroundScheduler);
+ });
+ });
+
+ it(@"should execute scheduled blocks immediately if it's in a scheduler already", ^{
+ __block BOOL done = NO;
+ __block BOOL executedImmediately = NO;
+
+ [[RACScheduler scheduler] schedule:^{
+ [RACScheduler.subscriptionScheduler schedule:^{
+ executedImmediately = YES;
+ }];
+
+ done = YES;
+ }];
+
+ expect(done).will.beTruthy();
+ expect(executedImmediately).to.beTruthy();
+ });
+});
+
+describe(@"+immediateScheduler", ^{
+ it(@"should immediately execute scheduled blocks", ^{
+ __block BOOL executed = NO;
+ RACDisposable *disposable = [RACScheduler.immediateScheduler schedule:^{
+ executed = YES;
+ }];
+
+ expect(disposable).to.beNil();
+ expect(executed).to.beTruthy();
+ });
+
+ it(@"should block for future scheduled blocks", ^{
+ __block BOOL executed = NO;
+ RACDisposable *disposable = [RACScheduler.immediateScheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{
+ executed = YES;
+ }];
+
+ expect(executed).to.beTruthy();
+ expect(disposable).to.beNil();
+ });
+});
+
+describe(@"-scheduleRecursiveBlock:", ^{
+ describe(@"with a synchronous scheduler", ^{
+ it(@"should behave like a normal block when it doesn't invoke itself", ^{
+ __block BOOL executed = NO;
+ [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ expect(executed).to.beFalsy();
+ executed = YES;
+ }];
+
+ expect(executed).to.beTruthy();
+ });
+
+ it(@"should reschedule itself after the caller completes", ^{
+ __block NSUInteger count = 0;
+ [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ NSUInteger thisCount = ++count;
+ if (thisCount < 3) {
+ recurse();
+
+ // The block shouldn't have been invoked again yet, only
+ // scheduled.
+ expect(count).to.equal(thisCount);
+ }
+ }];
+
+ expect(count).to.equal(3);
+ });
+ });
+
+ describe(@"with an asynchronous scheduler", ^{
+ it(@"should behave like a normal block when it doesn't invoke itself", ^{
+ __block BOOL executed = NO;
+ [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ expect(executed).to.beFalsy();
+ executed = YES;
+ }];
+
+ expect(executed).will.beTruthy();
+ });
+
+ it(@"should reschedule itself after the caller completes", ^{
+ __block NSUInteger count = 0;
+ [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ NSUInteger thisCount = ++count;
+ if (thisCount < 3) {
+ recurse();
+
+ // The block shouldn't have been invoked again yet, only
+ // scheduled.
+ expect(count).to.equal(thisCount);
+ }
+ }];
+
+ expect(count).will.equal(3);
+ });
+
+ it(@"should reschedule when invoked asynchronously", ^{
+ __block NSUInteger count = 0;
+
+ RACScheduler *asynchronousScheduler = [RACScheduler scheduler];
+ [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ [asynchronousScheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{
+ NSUInteger thisCount = ++count;
+ if (thisCount < 3) {
+ recurse();
+
+ // The block shouldn't have been invoked again yet, only
+ // scheduled.
+ expect(count).to.equal(thisCount);
+ }
+ }];
+ }];
+
+ expect(count).will.equal(3);
+ });
+
+ it(@"shouldn't reschedule itself when disposed", ^{
+ __block NSUInteger count = 0;
+ __block RACDisposable *disposable = [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) {
+ ++count;
+
+ expect(disposable).notTo.beNil();
+ [disposable dispose];
+
+ recurse();
+ }];
+
+ expect(count).will.equal(1);
+ });
+ });
+});
+
+describe(@"subclassing", ^{
+ __block RACTestExampleScheduler *scheduler;
+
+ beforeEach(^{
+ scheduler = [[RACTestExampleScheduler alloc] initWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
+ });
+
+ it(@"should invoke blocks scheduled with -schedule:", ^{
+ __block BOOL invoked = NO;
+ [scheduler schedule:^{
+ invoked = YES;
+ }];
+
+ expect(invoked).will.beTruthy();
+ });
+
+ it(@"should invoke blocks scheduled with -after:schedule:", ^{
+ __block BOOL invoked = NO;
+ [scheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{
+ invoked = YES;
+ }];
+
+ expect(invoked).will.beTruthy();
+ });
+
+ it(@"should set a valid current scheduler", ^{
+ __block RACScheduler *currentScheduler;
+ [scheduler schedule:^{
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+
+ expect(currentScheduler).will.equal(scheduler);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceAdditionsSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceAdditionsSpec.m
new file mode 100644
index 0000000..e5b5834
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceAdditionsSpec.m
@@ -0,0 +1,338 @@
+//
+// RACSequenceAdditionsSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequenceExamples.h"
+
+#import "NSArray+RACSequenceAdditions.h"
+#import "NSDictionary+RACSequenceAdditions.h"
+#import "NSOrderedSet+RACSequenceAdditions.h"
+#import "NSSet+RACSequenceAdditions.h"
+#import "NSString+RACSequenceAdditions.h"
+#import "NSIndexSet+RACSequenceAdditions.h"
+#import "RACSequence.h"
+#import "RACTuple.h"
+
+SpecBegin(RACSequenceAdditions)
+
+__block NSArray *numbers;
+
+beforeEach(^{
+ NSMutableArray *mutableNumbers = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++) {
+ [mutableNumbers addObject:@(i)];
+ }
+
+ numbers = [mutableNumbers copy];
+});
+
+describe(@"NSArray sequences", ^{
+ __block NSMutableArray *values;
+ __block RACSequence *sequence;
+
+ beforeEach(^{
+ values = [numbers mutableCopy];
+ sequence = values.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ __block NSArray *unchangedValues;
+
+ beforeEach(^{
+ unchangedValues = [values copy];
+ [values addObject:@6];
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: unchangedValues
+ };
+ });
+ });
+
+ it(@"should fast enumerate after zipping", ^{
+ // This certain list of values causes issues, for some reason.
+ NSArray *values = @[ @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0 ];
+ RACSequence *zippedSequence = [RACSequence zip:@[ values.rac_sequence, values.rac_sequence ] reduce:^(id obj1, id obj2) {
+ return obj1;
+ }];
+
+ NSMutableArray *collectedValues = [NSMutableArray array];
+ for (id value in zippedSequence) {
+ [collectedValues addObject:value];
+ }
+
+ expect(collectedValues).to.equal(values);
+ });
+});
+
+describe(@"NSDictionary sequences", ^{
+ __block NSMutableDictionary *dict;
+
+ __block NSMutableArray *tuples;
+ __block RACSequence *tupleSequence;
+
+ __block NSArray *keys;
+ __block RACSequence *keySequence;
+
+ __block NSArray *values;
+ __block RACSequence *valueSequence;
+
+ beforeEach(^{
+ dict = [@{
+ @"foo": @"bar",
+ @"baz": @"buzz",
+ @5: NSNull.null
+ } mutableCopy];
+
+ tuples = [NSMutableArray array];
+ for (id key in dict) {
+ RACTuple *tuple = [RACTuple tupleWithObjects:key, dict[key], nil];
+ [tuples addObject:tuple];
+ }
+
+ tupleSequence = dict.rac_sequence;
+ expect(tupleSequence).notTo.beNil();
+
+ keys = [dict.allKeys copy];
+ keySequence = dict.rac_keySequence;
+ expect(keySequence).notTo.beNil();
+
+ values = [dict.allValues copy];
+ valueSequence = dict.rac_valueSequence;
+ expect(valueSequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: tupleSequence,
+ RACSequenceExampleExpectedValues: tuples
+ };
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: keySequence,
+ RACSequenceExampleExpectedValues: keys
+ };
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: valueSequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ beforeEach(^{
+ dict[@"foo"] = @"rab";
+ dict[@6] = @7;
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: tupleSequence,
+ RACSequenceExampleExpectedValues: tuples
+ };
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: keySequence,
+ RACSequenceExampleExpectedValues: keys
+ };
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: valueSequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+ });
+});
+
+describe(@"NSOrderedSet sequences", ^{
+ __block NSMutableOrderedSet *values;
+ __block RACSequence *sequence;
+
+ beforeEach(^{
+ values = [NSMutableOrderedSet orderedSetWithArray:numbers];
+ sequence = values.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: values.array
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ __block NSArray *unchangedValues;
+
+ beforeEach(^{
+ unchangedValues = [values.array copy];
+ [values addObject:@6];
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: unchangedValues
+ };
+ });
+ });
+});
+
+describe(@"NSSet sequences", ^{
+ __block NSMutableSet *values;
+ __block RACSequence *sequence;
+
+ beforeEach(^{
+ values = [NSMutableSet setWithArray:numbers];
+ sequence = values.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: values.allObjects
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ __block NSArray *unchangedValues;
+
+ beforeEach(^{
+ unchangedValues = [values.allObjects copy];
+ [values addObject:@6];
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: unchangedValues
+ };
+ });
+ });
+});
+
+describe(@"NSString sequences", ^{
+ __block NSMutableString *string;
+ __block NSArray *values;
+ __block RACSequence *sequence;
+
+ beforeEach(^{
+ string = [@"foobar" mutableCopy];
+ values = @[ @"f", @"o", @"o", @"b", @"a", @"r" ];
+ sequence = string.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ beforeEach(^{
+ [string appendString:@"buzz"];
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+ });
+
+ it(@"should work with composed characters", ^{
+ NSString *string = @"\u2665\uFE0F\u2666\uFE0F";
+ NSArray *expectedSequence = @[ @"\u2665\uFE0F", @"\u2666\uFE0F" ];
+ expect(string.rac_sequence.array).to.equal(expectedSequence);
+ });
+});
+
+describe(@"RACTuple sequences", ^{
+ __block RACTuple *tuple;
+ __block RACSequence *sequence;
+
+ beforeEach(^{
+ tuple = RACTuplePack(@"foo", nil, @"bar", NSNull.null, RACTupleNil.tupleNil);
+
+ sequence = tuple.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: @[ @"foo", NSNull.null, @"bar", NSNull.null, NSNull.null ]
+ };
+ });
+});
+
+describe(@"NSIndexSet sequences", ^{
+ __block NSMutableIndexSet *values;
+ __block RACSequence *sequence;
+
+ NSArray * (^valuesFromIndexSet)(NSIndexSet *indexSet) = ^NSArray *(NSIndexSet *indexSet) {
+ NSMutableArray *arr = [NSMutableArray array];
+ [values enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
+ [arr addObject:@(idx)];
+ }];
+
+ return [arr copy];
+ };
+
+ beforeEach(^{
+ values = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 10)];
+ sequence = values.rac_sequence;
+ expect(sequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: valuesFromIndexSet(values)
+ };
+ });
+
+ describe(@"should be immutable", ^{
+ __block NSArray *unchangedValues;
+
+ beforeEach(^{
+ unchangedValues = valuesFromIndexSet(values);
+ [values addIndex:20];
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: unchangedValues
+ };
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.h
new file mode 100644
index 0000000..922b056
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.h
@@ -0,0 +1,16 @@
+//
+// RACSequenceExamples.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for RACSequence instances.
+extern NSString * const RACSequenceExamples;
+
+// RACSequence *
+extern NSString * const RACSequenceExampleSequence;
+
+// NSArray *
+extern NSString * const RACSequenceExampleExpectedValues;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.m
new file mode 100644
index 0000000..5ca3923
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.m
@@ -0,0 +1,125 @@
+//
+// RACSequenceExamples.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequenceExamples.h"
+
+#import "RACScheduler.h"
+#import "RACSequence.h"
+#import "RACSignal+Operations.h"
+
+NSString * const RACSequenceExamples = @"RACSequenceExamples";
+NSString * const RACSequenceExampleSequence = @"RACSequenceExampleSequence";
+NSString * const RACSequenceExampleExpectedValues = @"RACSequenceExampleExpectedValues";
+
+SharedExampleGroupsBegin(RACSequenceExamples);
+
+sharedExamplesFor(RACSequenceExamples, ^(NSDictionary *data) {
+ __block RACSequence *sequence;
+ __block NSArray *values;
+
+ beforeEach(^{
+ sequence = data[RACSequenceExampleSequence];
+ values = [data[RACSequenceExampleExpectedValues] copy];
+ });
+
+ it(@"should implement <NSFastEnumeration>", ^{
+ NSMutableArray *collectedValues = [NSMutableArray array];
+ for (id value in sequence) {
+ [collectedValues addObject:value];
+ }
+
+ expect(collectedValues).to.equal(values);
+ });
+
+ it(@"should return an array", ^{
+ expect(sequence.array).to.equal(values);
+ });
+
+ describe(@"-signalWithScheduler:", ^{
+ it(@"should return an immediately scheduled signal", ^{
+ RACSignal *signal = [sequence signalWithScheduler:RACScheduler.immediateScheduler];
+ expect(signal.toArray).to.equal(values);
+ });
+
+ it(@"should return a background scheduled signal", ^{
+ RACSignal *signal = [sequence signalWithScheduler:[RACScheduler scheduler]];
+ expect(signal.toArray).to.equal(values);
+ });
+
+ it(@"should only evaluate one value per scheduling", ^{
+ RACSignal *signal = [sequence signalWithScheduler:RACScheduler.mainThreadScheduler];
+
+ __block BOOL flag = YES;
+ __block BOOL completed = NO;
+ [signal subscribeNext:^(id x) {
+ expect(flag).to.beTruthy();
+ flag = NO;
+
+ [RACScheduler.mainThreadScheduler schedule:^{
+ // This should get executed before the next value (which
+ // verifies that it's YES).
+ flag = YES;
+ }];
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(completed).will.beTruthy();
+ });
+ });
+
+ it(@"should be equal to itself", ^{
+ expect(sequence).to.equal(sequence);
+ });
+
+ it(@"should be equal to the same sequence of values", ^{
+ RACSequence *newSequence = RACSequence.empty;
+ for (id value in values) {
+ RACSequence *valueSeq = [RACSequence return:value];
+ expect(valueSeq).notTo.beNil();
+
+ newSequence = [newSequence concat:valueSeq];
+ }
+
+ expect(sequence).to.equal(newSequence);
+ expect(sequence.hash).to.equal(newSequence.hash);
+ });
+
+ it(@"should not be equal to a different sequence of values", ^{
+ RACSequence *anotherSequence = [RACSequence return:@(-1)];
+ expect(sequence).notTo.equal(anotherSequence);
+ });
+
+ it(@"should return an identical object for -copy", ^{
+ expect([sequence copy]).to.beIdenticalTo(sequence);
+ });
+
+ it(@"should archive", ^{
+ NSData *data = [NSKeyedArchiver archivedDataWithRootObject:sequence];
+ expect(data).notTo.beNil();
+
+ RACSequence *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+ expect(unarchived).to.equal(sequence);
+ });
+
+ it(@"should fold right", ^{
+ RACSequence *result = [sequence foldRightWithStart:[RACSequence empty] reduce:^(id first, RACSequence *rest) {
+ return [rest.head startWith:first];
+ }];
+ expect(result.array).to.equal(values);
+ });
+
+ it(@"should fold left", ^{
+ RACSequence *result = [sequence foldLeftWithStart:[RACSequence empty] reduce:^(RACSequence *first, id rest) {
+ return [first concat:[RACSequence return:rest]];
+ }];
+ expect(result.array).to.equal(values);
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceSpec.m
new file mode 100644
index 0000000..3bf7dca
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceSpec.m
@@ -0,0 +1,443 @@
+//
+// RACSequenceSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSequenceExamples.h"
+#import "RACStreamExamples.h"
+
+#import "NSArray+RACSequenceAdditions.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSequence.h"
+#import "RACUnit.h"
+
+SpecBegin(RACSequence)
+
+describe(@"RACStream", ^{
+ id verifyValues = ^(RACSequence *sequence, NSArray *expectedValues) {
+ NSMutableArray *collectedValues = [NSMutableArray array];
+ while (sequence.head != nil) {
+ [collectedValues addObject:sequence.head];
+ sequence = sequence.tail;
+ }
+
+ expect(collectedValues).to.equal(expectedValues);
+ };
+
+ __block RACSequence *infiniteSequence = [RACSequence sequenceWithHeadBlock:^{
+ return RACUnit.defaultUnit;
+ } tailBlock:^{
+ return infiniteSequence;
+ }];
+
+ itShouldBehaveLike(RACStreamExamples, ^{
+ return @{
+ RACStreamExamplesClass: RACSequence.class,
+ RACStreamExamplesVerifyValuesBlock: verifyValues,
+ RACStreamExamplesInfiniteStream: infiniteSequence
+ };
+ });
+});
+
+describe(@"+sequenceWithHeadBlock:tailBlock:", ^{
+ __block RACSequence *sequence;
+ __block BOOL headInvoked;
+ __block BOOL tailInvoked;
+
+ before(^{
+ headInvoked = NO;
+ tailInvoked = NO;
+
+ sequence = [RACSequence sequenceWithHeadBlock:^{
+ headInvoked = YES;
+ return @0;
+ } tailBlock:^{
+ tailInvoked = YES;
+ return [RACSequence return:@1];
+ }];
+
+ expect(sequence).notTo.beNil();
+ });
+
+ it(@"should use the values from the head and tail blocks", ^{
+ expect(sequence.head).to.equal(@0);
+ expect(sequence.tail.head).to.equal(@1);
+ expect(sequence.tail.tail).to.beNil();
+ });
+
+ it(@"should lazily invoke head and tail blocks", ^{
+ expect(headInvoked).to.beFalsy();
+ expect(tailInvoked).to.beFalsy();
+
+ expect(sequence.head).to.equal(@0);
+ expect(headInvoked).to.beTruthy();
+ expect(tailInvoked).to.beFalsy();
+
+ expect(sequence.tail).notTo.beNil();
+ expect(tailInvoked).to.beTruthy();
+ });
+
+ after(^{
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: sequence,
+ RACSequenceExampleExpectedValues: @[ @0, @1 ]
+ };
+ });
+ });
+});
+
+describe(@"empty sequences", ^{
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: [RACSequence empty],
+ RACSequenceExampleExpectedValues: @[]
+ };
+ });
+});
+
+describe(@"non-empty sequences", ^{
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]],
+ RACSequenceExampleExpectedValues: @[ @0, @1, @2 ]
+ };
+ });
+});
+
+describe(@"eager sequences", ^{
+ __block RACSequence *lazySequence;
+ __block BOOL headInvoked;
+ __block BOOL tailInvoked;
+
+ NSArray *values = @[ @0, @1 ];
+
+ before(^{
+ headInvoked = NO;
+ tailInvoked = NO;
+
+ lazySequence = [RACSequence sequenceWithHeadBlock:^{
+ headInvoked = YES;
+ return @0;
+ } tailBlock:^{
+ tailInvoked = YES;
+ return [RACSequence return:@1];
+ }];
+
+ expect(lazySequence).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: lazySequence.eagerSequence,
+ RACSequenceExampleExpectedValues: values
+ };
+ });
+
+ it(@"should evaluate all values immediately", ^{
+ RACSequence *eagerSequence = lazySequence.eagerSequence;
+ expect(headInvoked).to.beTruthy();
+ expect(tailInvoked).to.beTruthy();
+ expect(eagerSequence.array).to.equal(values);
+ });
+});
+
+describe(@"-take:", ^{
+ it(@"should complete take: without needing the head of the second item in the sequence", ^{
+ __block NSUInteger valuesTaken = 0;
+
+ __block RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{
+ ++valuesTaken;
+ return RACUnit.defaultUnit;
+ } tailBlock:^{
+ return sequence;
+ }];
+
+ NSArray *values = [sequence take:1].array;
+ expect(values).to.equal(@[ RACUnit.defaultUnit ]);
+ expect(valuesTaken).to.equal(1);
+ });
+});
+
+describe(@"-bind:", ^{
+ it(@"should only evaluate head when the resulting sequence is evaluated", ^{
+ __block BOOL headInvoked = NO;
+
+ RACSequence *original = [RACSequence sequenceWithHeadBlock:^{
+ headInvoked = YES;
+ return RACUnit.defaultUnit;
+ } tailBlock:^ id {
+ return nil;
+ }];
+
+ RACSequence *bound = [original bind:^{
+ return ^(id value, BOOL *stop) {
+ return [RACSequence return:value];
+ };
+ }];
+
+ expect(bound).notTo.beNil();
+ expect(headInvoked).to.beFalsy();
+
+ expect(bound.head).to.equal(RACUnit.defaultUnit);
+ expect(headInvoked).to.beTruthy();
+ });
+});
+
+describe(@"-objectEnumerator", ^{
+ it(@"should only evaluate head as it's enumerated", ^{
+ __block BOOL firstHeadInvoked = NO;
+ __block BOOL secondHeadInvoked = NO;
+ __block BOOL thirdHeadInvoked = NO;
+
+ RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^id{
+ firstHeadInvoked = YES;
+ return @1;
+ } tailBlock:^RACSequence *{
+ return [RACSequence sequenceWithHeadBlock:^id{
+ secondHeadInvoked = YES;
+ return @2;
+ } tailBlock:^RACSequence *{
+ return [RACSequence sequenceWithHeadBlock:^id{
+ thirdHeadInvoked = YES;
+ return @3;
+ } tailBlock:^RACSequence *{
+ return RACSequence.empty;
+ }];
+ }];
+ }];
+ NSEnumerator *enumerator = sequence.objectEnumerator;
+
+ expect(firstHeadInvoked).to.beFalsy();
+ expect(secondHeadInvoked).to.beFalsy();
+ expect(thirdHeadInvoked).to.beFalsy();
+
+ expect([enumerator nextObject]).to.equal(@1);
+
+ expect(firstHeadInvoked).to.beTruthy();
+ expect(secondHeadInvoked).to.beFalsy();
+ expect(thirdHeadInvoked).to.beFalsy();
+
+ expect([enumerator nextObject]).to.equal(@2);
+
+ expect(secondHeadInvoked).to.beTruthy();
+ expect(thirdHeadInvoked).to.beFalsy();
+
+ expect([enumerator nextObject]).to.equal(@3);
+
+ expect(thirdHeadInvoked).to.beTruthy();
+
+ expect([enumerator nextObject]).to.beNil();
+ });
+
+ it(@"should let the sequence dealloc as it's enumerated", ^{
+ __block BOOL firstSequenceDeallocd = NO;
+ __block BOOL secondSequenceDeallocd = NO;
+ __block BOOL thirdSequenceDeallocd = NO;
+
+ NSEnumerator *enumerator = nil;
+
+ @autoreleasepool {
+ RACSequence *thirdSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
+ return @3;
+ } tailBlock:^RACSequence *{
+ return RACSequence.empty;
+ }];
+ [thirdSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ thirdSequenceDeallocd = YES;
+ }]];
+
+ RACSequence *secondSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
+ return @2;
+ } tailBlock:^RACSequence *{
+ return thirdSequence;
+ }];
+ [secondSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ secondSequenceDeallocd = YES;
+ }]];
+
+ RACSequence *firstSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{
+ return @1;
+ } tailBlock:^RACSequence *{
+ return secondSequence;
+ }];
+ [firstSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ firstSequenceDeallocd = YES;
+ }]];
+
+ enumerator = firstSequence.objectEnumerator;
+ }
+
+ @autoreleasepool {
+ expect([enumerator nextObject]).to.equal(@1);
+ }
+
+ @autoreleasepool {
+ expect([enumerator nextObject]).to.equal(@2);
+ }
+ expect(firstSequenceDeallocd).will.beTruthy();
+
+ @autoreleasepool {
+ expect([enumerator nextObject]).to.equal(@3);
+ }
+ expect(secondSequenceDeallocd).will.beTruthy();
+
+ @autoreleasepool {
+ expect([enumerator nextObject]).to.beNil();
+ }
+ expect(thirdSequenceDeallocd).will.beTruthy();
+ });
+});
+
+it(@"shouldn't overflow the stack when deallocated on a background queue", ^{
+ NSUInteger length = 10000;
+ NSMutableArray *values = [NSMutableArray arrayWithCapacity:length];
+ for (NSUInteger i = 0; i < length; ++i) {
+ [values addObject:@(i)];
+ }
+
+ __block BOOL finished = NO;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ @autoreleasepool {
+ [[values.rac_sequence map:^(id value) {
+ return value;
+ }] array];
+ }
+
+ finished = YES;
+ });
+
+ NSTimeInterval oldTimeout = Expecta.asynchronousTestTimeout;
+ Expecta.asynchronousTestTimeout = DBL_MAX;
+ expect(finished).will.beTruthy();
+ Expecta.asynchronousTestTimeout = oldTimeout;
+});
+
+describe(@"-foldLeftWithStart:reduce:", ^{
+ it(@"should reduce with start first", ^{
+ RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
+ NSNumber *result = [sequence foldLeftWithStart:@3 reduce:^(NSNumber *first, NSNumber *rest) {
+ return first;
+ }];
+ expect(result).to.equal(@3);
+ });
+
+ it(@"should be left associative", ^{
+ RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]];
+ NSNumber *result = [sequence foldLeftWithStart:@0 reduce:^(NSNumber *first, NSNumber *rest) {
+ int difference = first.intValue - rest.intValue;
+ return @(difference);
+ }];
+ expect(result).to.equal(@-6);
+ });
+});
+
+describe(@"-foldRightWithStart:reduce:", ^{
+ it(@"should be lazy", ^{
+ __block BOOL headInvoked = NO;
+ __block BOOL tailInvoked = NO;
+ RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{
+ headInvoked = YES;
+ return @0;
+ } tailBlock:^{
+ tailInvoked = YES;
+ return [RACSequence return:@1];
+ }];
+
+ NSNumber *result = [sequence foldRightWithStart:@2 reduce:^(NSNumber *first, RACSequence *rest) {
+ return first;
+ }];
+
+ expect(result).to.equal(@0);
+ expect(headInvoked).to.beTruthy();
+ expect(tailInvoked).to.beFalsy();
+ });
+
+ it(@"should reduce with start last", ^{
+ RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
+ NSNumber *result = [sequence foldRightWithStart:@3 reduce:^(NSNumber *first, RACSequence *rest) {
+ return rest.head;
+ }];
+ expect(result).to.equal(@3);
+ });
+
+ it(@"should be right associative", ^{
+ RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]];
+ NSNumber *result = [sequence foldRightWithStart:@0 reduce:^(NSNumber *first, RACSequence *rest) {
+ int difference = first.intValue - [rest.head intValue];
+ return @(difference);
+ }];
+ expect(result).to.equal(@2);
+ });
+});
+
+describe(@"-any", ^{
+ __block RACSequence *sequence;
+ beforeEach(^{
+ sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
+ });
+
+ it(@"should return true when at least one exists", ^{
+ BOOL result = [sequence any:^ BOOL (NSNumber *value) {
+ return value.integerValue > 0;
+ }];
+ expect(result).to.beTruthy();
+ });
+
+ it(@"should return false when no such thing exists", ^{
+ BOOL result = [sequence any:^ BOOL (NSNumber *value) {
+ return value.integerValue == 3;
+ }];
+ expect(result).to.beFalsy();
+ });
+});
+
+describe(@"-all", ^{
+ __block RACSequence *sequence;
+ beforeEach(^{
+ sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
+ });
+
+ it(@"should return true when all values pass", ^{
+ BOOL result = [sequence all:^ BOOL (NSNumber *value) {
+ return value.integerValue >= 0;
+ }];
+ expect(result).to.beTruthy();
+ });
+
+ it(@"should return false when at least one value fails", ^{
+ BOOL result = [sequence all:^ BOOL (NSNumber *value) {
+ return value.integerValue < 2;
+ }];
+ expect(result).to.beFalsy();
+ });
+});
+
+describe(@"-objectPassingTest:", ^{
+ __block RACSequence *sequence;
+ beforeEach(^{
+ sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]];
+ });
+
+ it(@"should return leftmost object that passes the test", ^{
+ NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) {
+ return value.intValue > 0;
+ }];
+ expect(result).to.equal(@1);
+ });
+
+ it(@"should return nil if no objects pass the test", ^{
+ NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) {
+ return value.intValue < 0;
+ }];
+ expect(result).to.beNil();
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSerialDisposableSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSerialDisposableSpec.m
new file mode 100644
index 0000000..cdf35f5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSerialDisposableSpec.m
@@ -0,0 +1,137 @@
+//
+// RACSerialDisposableSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSerialDisposable.h"
+
+SpecBegin(RACSerialDisposable)
+
+it(@"should initialize with -init", ^{
+ RACSerialDisposable *serial = [[RACSerialDisposable alloc] init];
+ expect(serial).notTo.beNil();
+ expect(serial.disposable).to.beNil();
+});
+
+it(@"should initialize an inner disposable with -initWithBlock:", ^{
+ __block BOOL disposed = NO;
+ RACSerialDisposable *serial = [RACSerialDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ expect(serial).notTo.beNil();
+ expect(serial.disposable).notTo.beNil();
+
+ [serial.disposable dispose];
+ expect(serial.disposed).to.beFalsy();
+ expect(disposed).to.beTruthy();
+});
+
+it(@"should initialize with a disposable", ^{
+ RACDisposable *inner = [[RACDisposable alloc] init];
+ RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
+ expect(serial).notTo.beNil();
+ expect(serial.disposable).to.equal(inner);
+});
+
+it(@"should dispose of the inner disposable", ^{
+ __block BOOL disposed = NO;
+ RACDisposable *inner = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
+ expect(serial.disposed).to.beFalsy();
+ expect(disposed).to.beFalsy();
+
+ [serial dispose];
+ expect(serial.disposed).to.beTruthy();
+ expect(serial.disposable).to.beNil();
+ expect(disposed).to.beTruthy();
+});
+
+it(@"should dispose of a new inner disposable if it's already been disposed", ^{
+ __block BOOL disposed = NO;
+ RACDisposable *inner = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ RACSerialDisposable *serial = [[RACSerialDisposable alloc] init];
+ expect(serial.disposed).to.beFalsy();
+
+ [serial dispose];
+ expect(serial.disposed).to.beTruthy();
+ expect(disposed).to.beFalsy();
+
+ serial.disposable = inner;
+ expect(disposed).to.beTruthy();
+ expect(serial.disposable).to.beNil();
+});
+
+it(@"should allow the inner disposable to be set to nil", ^{
+ __block BOOL disposed = NO;
+ RACDisposable *inner = [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+
+ RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner];
+ expect(disposed).to.beFalsy();
+
+ serial.disposable = nil;
+ expect(serial.disposable).to.beNil();
+
+ serial.disposable = inner;
+ expect(serial.disposable).to.equal(inner);
+
+ [serial dispose];
+ expect(disposed).to.beTruthy();
+ expect(serial.disposable).to.beNil();
+});
+
+it(@"should swap inner disposables", ^{
+ __block BOOL firstDisposed = NO;
+ RACDisposable *first = [RACDisposable disposableWithBlock:^{
+ firstDisposed = YES;
+ }];
+
+ __block BOOL secondDisposed = NO;
+ RACDisposable *second = [RACDisposable disposableWithBlock:^{
+ secondDisposed = YES;
+ }];
+
+ RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:first];
+ expect([serial swapInDisposable:second]).to.equal(first);
+
+ expect(serial.disposed).to.beFalsy();
+ expect(firstDisposed).to.beFalsy();
+ expect(secondDisposed).to.beFalsy();
+
+ [serial dispose];
+ expect(serial.disposed).to.beTruthy();
+ expect(serial.disposable).to.beNil();
+
+ expect(firstDisposed).to.beFalsy();
+ expect(secondDisposed).to.beTruthy();
+});
+
+it(@"should release the inner disposable upon deallocation", ^{
+ __weak RACDisposable *weakInnerDisposable;
+ __weak RACSerialDisposable *weakSerialDisposable;
+
+ @autoreleasepool {
+ RACDisposable *innerDisposable __attribute__((objc_precise_lifetime)) = [[RACDisposable alloc] init];
+ weakInnerDisposable = innerDisposable;
+
+ RACSerialDisposable *serialDisposable __attribute__((objc_precise_lifetime)) = [[RACSerialDisposable alloc] init];
+ serialDisposable.disposable = innerDisposable;
+ weakSerialDisposable = serialDisposable;
+ }
+
+ expect(weakSerialDisposable).to.beNil();
+ expect(weakInnerDisposable).to.beNil();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalSpec.m
new file mode 100644
index 0000000..21b50dd
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalSpec.m
@@ -0,0 +1,3877 @@
+//
+// RACSignalSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/2/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACPropertySignalExamples.h"
+#import "RACSequenceExamples.h"
+#import "RACStreamExamples.h"
+#import "RACTestObject.h"
+
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACDeallocating.h"
+#import "NSObject+RACPropertySubscribing.h"
+#import "RACBehaviorSubject.h"
+#import "RACCommand.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACEvent.h"
+#import "RACGroupedSignal.h"
+#import "RACMulticastConnection.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import "RACSignal+Operations.h"
+#import "RACSignalStartExamples.h"
+#import "RACSubject.h"
+#import "RACSubscriber+Private.h"
+#import "RACSubscriber.h"
+#import "RACTestScheduler.h"
+#import "RACTuple.h"
+#import "RACUnit.h"
+#import <libkern/OSAtomic.h>
+
+// Set in a beforeAll below.
+static NSError *RACSignalTestError;
+
+static NSString * const RACSignalMergeConcurrentCompletionExampleGroup = @"RACSignalMergeConcurrentCompletionExampleGroup";
+static NSString * const RACSignalMaxConcurrent = @"RACSignalMaxConcurrent";
+SharedExampleGroupsBegin(mergeConcurrentCompletionName);
+
+sharedExamplesFor(RACSignalMergeConcurrentCompletionExampleGroup, ^(NSDictionary *data) {
+ it(@"should complete only after the source and all its signals have completed", ^{
+ RACSubject *subject1 = [RACSubject subject];
+ RACSubject *subject2 = [RACSubject subject];
+ RACSubject *subject3 = [RACSubject subject];
+
+ RACSubject *signalsSubject = [RACSubject subject];
+ __block BOOL completed = NO;
+ [[signalsSubject flatten:[data[RACSignalMaxConcurrent] unsignedIntegerValue]] subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ [signalsSubject sendNext:subject1];
+ [subject1 sendCompleted];
+
+ expect(completed).to.beFalsy();
+
+ [signalsSubject sendNext:subject2];
+ [signalsSubject sendNext:subject3];
+
+ [signalsSubject sendCompleted];
+
+ expect(completed).to.beFalsy();
+
+ [subject2 sendCompleted];
+
+ expect(completed).to.beFalsy();
+
+ [subject3 sendCompleted];
+
+ expect(completed).to.beTruthy();
+ });
+});
+
+SharedExampleGroupsEnd
+
+SpecBegin(RACSignal)
+
+beforeAll(^{
+ // We do this instead of a macro to ensure that to.equal() will work
+ // correctly (by matching identity), even if -[NSError isEqual:] is broken.
+ RACSignalTestError = [NSError errorWithDomain:@"foo" code:100 userInfo:nil];
+});
+
+describe(@"RACStream", ^{
+ id verifyValues = ^(RACSignal *signal, NSArray *expectedValues) {
+ expect(signal).notTo.beNil();
+
+ NSMutableArray *collectedValues = [NSMutableArray array];
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ [signal subscribeNext:^(id value) {
+ [collectedValues addObject:value];
+ } error:^(NSError *receivedError) {
+ error = receivedError;
+ } completed:^{
+ success = YES;
+ }];
+
+ expect(success).will.beTruthy();
+ expect(error).to.beNil();
+ expect(collectedValues).to.equal(expectedValues);
+ };
+
+ RACSignal *infiniteSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ __block volatile int32_t done = 0;
+
+ [RACScheduler.mainThreadScheduler schedule:^{
+ while (!done) {
+ [subscriber sendNext:RACUnit.defaultUnit];
+ }
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ OSAtomicIncrement32Barrier(&done);
+ }];
+ }];
+
+ itShouldBehaveLike(RACStreamExamples, ^{
+ return @{
+ RACStreamExamplesClass: RACSignal.class,
+ RACStreamExamplesVerifyValuesBlock: verifyValues,
+ RACStreamExamplesInfiniteStream: infiniteSignal
+ };
+ });
+});
+
+describe(@"-bind:", ^{
+ __block RACSubject *signals;
+ __block BOOL disposed;
+ __block id lastValue;
+ __block RACSubject *values;
+
+ beforeEach(^{
+ // Tests send a (RACSignal, BOOL) pair that are used below in -bind:.
+ signals = [RACSubject subject];
+
+ disposed = NO;
+ RACSignal *source = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ [signals subscribe:subscriber];
+
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ RACSignal *bind = [source bind:^{
+ return ^(RACTuple *x, BOOL *stop) {
+ RACTupleUnpack(RACSignal *signal, NSNumber *stopValue) = x;
+ *stop = stopValue.boolValue;
+ return signal;
+ };
+ }];
+
+ lastValue = nil;
+ [bind subscribeNext:^(id x) {
+ lastValue = x;
+ }];
+
+ // Send `bind` an open ended subject to subscribe to. These tests make
+ // use of this in two ways:
+ // 1. Used to test a regression bug where -bind: would not actually
+ // stop when instructed to. This bug manifested itself only when
+ // there were subscriptions that lived on past the point at which
+ // -bind: was stopped. This subject represents such a subscription.
+ // 2. Test that values sent by this subject are received by `bind`'s
+ // subscriber, even *after* -bind: has been instructed to stop.
+ values = [RACSubject subject];
+ [signals sendNext:RACTuplePack(values, @NO)];
+ expect(disposed).to.beFalsy();
+ });
+
+ it(@"should dispose source signal when stopped with nil signal", ^{
+ // Tell -bind: to stop by sending it a `nil` signal.
+ [signals sendNext:RACTuplePack(nil, @NO)];
+ expect(disposed).to.beTruthy();
+
+ // Should still receive values sent after stopping.
+ expect(lastValue).to.beNil();
+ [values sendNext:RACUnit.defaultUnit];
+ expect(lastValue).to.equal(RACUnit.defaultUnit);
+ });
+
+ it(@"should dispose source signal when stop flag set to YES", ^{
+ // Tell -bind: to stop by setting the stop flag to YES.
+ [signals sendNext:RACTuplePack([RACSignal return:@1], @YES)];
+ expect(disposed).to.beTruthy();
+
+ // Should still recieve last signal sent at the time of setting stop to YES.
+ expect(lastValue).to.equal(@1);
+
+ // Should still receive values sent after stopping.
+ [values sendNext:@2];
+ expect(lastValue).to.equal(@2);
+ });
+});
+
+describe(@"subscribing", ^{
+ __block RACSignal *signal = nil;
+ id nextValueSent = @"1";
+
+ beforeEach(^{
+ signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:nextValueSent];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+ });
+
+ it(@"should get next values", ^{
+ __block id nextValueReceived = nil;
+ [signal subscribeNext:^(id x) {
+ nextValueReceived = x;
+ } error:^(NSError *error) {
+
+ } completed:^{
+
+ }];
+
+ expect(nextValueReceived).to.equal(nextValueSent);
+ });
+
+ it(@"should get completed", ^{
+ __block BOOL didGetCompleted = NO;
+ [signal subscribeNext:^(id x) {
+
+ } error:^(NSError *error) {
+
+ } completed:^{
+ didGetCompleted = YES;
+ }];
+
+ expect(didGetCompleted).to.beTruthy();
+ });
+
+ it(@"should not get an error", ^{
+ __block BOOL didGetError = NO;
+ [signal subscribeNext:^(id x) {
+
+ } error:^(NSError *error) {
+ didGetError = YES;
+ } completed:^{
+
+ }];
+
+ expect(didGetError).to.beFalsy();
+ });
+
+ it(@"shouldn't get anything after dispose", ^{
+ RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];
+ NSMutableArray *receivedValues = [NSMutableArray array];
+
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@0];
+
+ [scheduler afterDelay:0 schedule:^{
+ [subscriber sendNext:@1];
+ }];
+
+ return nil;
+ }];
+
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ NSArray *expectedValues = @[ @0 ];
+ expect(receivedValues).to.equal(expectedValues);
+
+ [disposable dispose];
+ [scheduler stepAll];
+
+ expect(receivedValues).to.equal(expectedValues);
+ });
+
+ it(@"should have a current scheduler in didSubscribe block", ^{
+ __block RACScheduler *currentScheduler;
+ RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ currentScheduler = RACScheduler.currentScheduler;
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ [signal subscribeNext:^(id x) {}];
+ expect(currentScheduler).notTo.beNil();
+
+ currentScheduler = nil;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [signal subscribeNext:^(id x) {}];
+ });
+ expect(currentScheduler).willNot.beNil();
+ });
+
+ it(@"should automatically dispose of other subscriptions from +createSignal:", ^{
+ __block BOOL innerDisposed = NO;
+
+ RACSignal *innerSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ innerDisposed = YES;
+ }];
+ }];
+
+ RACSignal *outerSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [innerSignal subscribe:subscriber];
+ return nil;
+ }];
+
+ RACDisposable *disposable = [outerSignal subscribeCompleted:^{}];
+ expect(disposable).notTo.beNil();
+ expect(innerDisposed).to.beFalsy();
+
+ [disposable dispose];
+ expect(innerDisposed).to.beTruthy();
+ });
+});
+
+describe(@"-takeUntil:", ^{
+ it(@"should support value as trigger", ^{
+ __block BOOL shouldBeGettingItems = YES;
+ RACSubject *subject = [RACSubject subject];
+ RACSubject *cutOffSubject = [RACSubject subject];
+ [[subject takeUntil:cutOffSubject] subscribeNext:^(id x) {
+ expect(shouldBeGettingItems).to.beTruthy();
+ }];
+
+ shouldBeGettingItems = YES;
+ [subject sendNext:@"test 1"];
+ [subject sendNext:@"test 2"];
+
+ [cutOffSubject sendNext:[RACUnit defaultUnit]];
+
+ shouldBeGettingItems = NO;
+ [subject sendNext:@"test 3"];
+ });
+
+ it(@"should support completion as trigger", ^{
+ __block BOOL shouldBeGettingItems = YES;
+ RACSubject *subject = [RACSubject subject];
+ RACSubject *cutOffSubject = [RACSubject subject];
+ [[subject takeUntil:cutOffSubject] subscribeNext:^(id x) {
+ expect(shouldBeGettingItems).to.beTruthy();
+ }];
+
+ [cutOffSubject sendCompleted];
+
+ 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(@"-takeUntilReplacement:", ^{
+ it(@"should forward values from the receiver until it's replaced", ^{
+ RACSubject *receiver = [RACSubject subject];
+ RACSubject *replacement = [RACSubject subject];
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+
+ [[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ expect(receivedValues).to.equal(@[]);
+
+ [receiver sendNext:@1];
+ expect(receivedValues).to.equal(@[ @1 ]);
+
+ [receiver sendNext:@2];
+ expect(receivedValues).to.equal((@[ @1, @2 ]));
+
+ [replacement sendNext:@3];
+ expect(receivedValues).to.equal((@[ @1, @2, @3 ]));
+
+ [receiver sendNext:@4];
+ expect(receivedValues).to.equal((@[ @1, @2, @3 ]));
+
+ [replacement sendNext:@5];
+ expect(receivedValues).to.equal((@[ @1, @2, @3, @5 ]));
+ });
+
+ it(@"should forward error from the receiver", ^{
+ RACSubject *receiver = [RACSubject subject];
+ __block BOOL receivedError = NO;
+
+ [[receiver takeUntilReplacement:RACSignal.never] subscribeError:^(NSError *error) {
+ receivedError = YES;
+ }];
+
+ [receiver sendError:nil];
+ expect(receivedError).to.beTruthy();
+ });
+
+ it(@"should not forward completed from the receiver", ^{
+ RACSubject *receiver = [RACSubject subject];
+ __block BOOL receivedCompleted = NO;
+
+ [[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted: ^{
+ receivedCompleted = YES;
+ }];
+
+ [receiver sendCompleted];
+ expect(receivedCompleted).to.beFalsy();
+ });
+
+ it(@"should forward error from the replacement signal", ^{
+ RACSubject *replacement = [RACSubject subject];
+ __block BOOL receivedError = NO;
+
+ [[RACSignal.never takeUntilReplacement:replacement] subscribeError:^(NSError *error) {
+ receivedError = YES;
+ }];
+
+ [replacement sendError:nil];
+ expect(receivedError).to.beTruthy();
+ });
+
+ it(@"should forward completed from the replacement signal", ^{
+ RACSubject *replacement = [RACSubject subject];
+ __block BOOL receivedCompleted = NO;
+
+ [[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted: ^{
+ receivedCompleted = YES;
+ }];
+
+ [replacement sendCompleted];
+ expect(receivedCompleted).to.beTruthy();
+ });
+
+ it(@"should not forward values from the receiver if both send synchronously", ^{
+ RACSignal *receiver = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ [subscriber sendNext:@3];
+ return nil;
+ }];
+ RACSignal *replacement = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@4];
+ [subscriber sendNext:@5];
+ [subscriber sendNext:@6];
+ return nil;
+ }];
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+
+ [[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ expect(receivedValues).to.equal((@[ @4, @5, @6 ]));
+ });
+
+ it(@"should dispose of the receiver when it's disposed of", ^{
+ __block BOOL receiverDisposed = NO;
+ RACSignal *receiver = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ receiverDisposed = YES;
+ }];
+ }];
+
+ [[[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted:^{}] dispose];
+
+ expect(receiverDisposed).to.beTruthy();
+ });
+
+ it(@"should dispose of the replacement signal when it's disposed of", ^{
+ __block BOOL replacementDisposed = NO;
+ RACSignal *replacement = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ replacementDisposed = YES;
+ }];
+ }];
+
+ [[[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted:^{}] dispose];
+
+ expect(replacementDisposed).to.beTruthy();
+ });
+
+ it(@"should dispose of the receiver when the replacement signal sends an event", ^{
+ __block BOOL receiverDisposed = NO;
+ RACSignal *receiver = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ receiverDisposed = YES;
+ }];
+ }];
+ RACSubject *replacement = [RACSubject subject];
+
+ [[receiver takeUntilReplacement:replacement] subscribeCompleted:^{}];
+
+ expect(receiverDisposed).to.beFalsy();
+
+ [replacement sendNext:nil];
+
+ expect(receiverDisposed).to.beTruthy();
+ });
+});
+
+describe(@"disposal", ^{
+ it(@"should dispose of the didSubscribe disposable", ^{
+ __block BOOL innerDisposed = NO;
+ RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ innerDisposed = YES;
+ }];
+ }];
+
+ expect(innerDisposed).to.beFalsy();
+
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {}];
+ expect(disposable).notTo.beNil();
+
+ [disposable dispose];
+ expect(innerDisposed).to.beTruthy();
+ });
+
+ it(@"should dispose of the didSubscribe disposable asynchronously", ^{
+ __block BOOL innerDisposed = NO;
+ RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ innerDisposed = YES;
+ }];
+ }];
+
+ [[RACScheduler scheduler] schedule:^{
+ RACDisposable *disposable = [signal subscribeNext:^(id x) {}];
+ [disposable dispose];
+ }];
+
+ expect(innerDisposed).will.beTruthy();
+ });
+});
+
+describe(@"querying", ^{
+ __block RACSignal *signal = nil;
+ id nextValueSent = @"1";
+
+ beforeEach(^{
+ signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:nextValueSent];
+ [subscriber sendNext:@"other value"];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+ });
+
+ it(@"should return first 'next' value with -firstOrDefault:success:error:", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ [subscriber sendNext:@3];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ expect(signal).notTo.beNil();
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@1);
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ });
+
+ it(@"should return first default value with -firstOrDefault:success:error:", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ expect(signal).notTo.beNil();
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ });
+
+ it(@"should return error with -firstOrDefault:success:error:", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendError:RACSignalTestError];
+ return nil;
+ }];
+
+ expect(signal).notTo.beNil();
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
+ expect(success).to.beFalsy();
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"shouldn't crash when returning an error from a background scheduler", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [[RACScheduler scheduler] schedule:^{
+ [subscriber sendError:RACSignalTestError];
+ }];
+
+ return nil;
+ }];
+
+ expect(signal).notTo.beNil();
+
+ __block BOOL success = NO;
+ __block NSError *error = nil;
+ expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
+ expect(success).to.beFalsy();
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should terminate the subscription after returning from -firstOrDefault:success:error:", ^{
+ __block BOOL disposed = NO;
+ RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ [subscriber sendNext:RACUnit.defaultUnit];
+
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ expect(signal).notTo.beNil();
+ expect(disposed).to.beFalsy();
+
+ expect([signal firstOrDefault:nil success:NULL error:NULL]).to.equal(RACUnit.defaultUnit);
+ expect(disposed).to.beTruthy();
+ });
+
+ it(@"should return YES from -waitUntilCompleted: when successful", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:RACUnit.defaultUnit];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ __block NSError *error = nil;
+ expect([signal waitUntilCompleted:&error]).to.beTruthy();
+ expect(error).to.beNil();
+ });
+
+ it(@"should return NO from -waitUntilCompleted: upon error", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:RACUnit.defaultUnit];
+ [subscriber sendError:RACSignalTestError];
+ return nil;
+ }];
+
+ __block NSError *error = nil;
+ expect([signal waitUntilCompleted:&error]).to.beFalsy();
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should return a delayed value from -asynchronousFirstOrDefault:success:error:", ^{
+ RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0];
+
+ __block BOOL scheduledBlockRan = NO;
+ [RACScheduler.mainThreadScheduler schedule:^{
+ scheduledBlockRan = YES;
+ }];
+
+ expect(scheduledBlockRan).to.beFalsy();
+
+ BOOL success = NO;
+ NSError *error = nil;
+ id value = [signal asynchronousFirstOrDefault:nil success:&success error:&error];
+
+ expect(scheduledBlockRan).to.beTruthy();
+
+ expect(value).to.equal(RACUnit.defaultUnit);
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ });
+
+ it(@"should return a default value from -asynchronousFirstOrDefault:success:error:", ^{
+ RACSignal *signal = [[RACSignal error:RACSignalTestError] delay:0];
+
+ __block BOOL scheduledBlockRan = NO;
+ [RACScheduler.mainThreadScheduler schedule:^{
+ scheduledBlockRan = YES;
+ }];
+
+ expect(scheduledBlockRan).to.beFalsy();
+
+ BOOL success = NO;
+ NSError *error = nil;
+ id value = [signal asynchronousFirstOrDefault:RACUnit.defaultUnit success:&success error:&error];
+
+ expect(scheduledBlockRan).to.beTruthy();
+
+ expect(value).to.equal(RACUnit.defaultUnit);
+ expect(success).to.beFalsy();
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should return a delayed error from -asynchronousFirstOrDefault:success:error:", ^{
+ RACSignal *signal = [[RACSignal
+ createSignal:^(id<RACSubscriber> subscriber) {
+ return [[RACScheduler scheduler] schedule:^{
+ [subscriber sendError:RACSignalTestError];
+ }];
+ }]
+ deliverOn:RACScheduler.mainThreadScheduler];
+
+ __block NSError *error = nil;
+ __block BOOL success = NO;
+ expect([signal asynchronousFirstOrDefault:nil success:&success error:&error]).to.beNil();
+
+ expect(success).to.beFalsy();
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should terminate the subscription after returning from -asynchronousFirstOrDefault:success:error:", ^{
+ __block BOOL disposed = NO;
+ RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ [[RACScheduler scheduler] schedule:^{
+ [subscriber sendNext:RACUnit.defaultUnit];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ expect(signal).notTo.beNil();
+ expect(disposed).to.beFalsy();
+
+ expect([signal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(RACUnit.defaultUnit);
+ expect(disposed).will.beTruthy();
+ });
+
+ it(@"should return a delayed success from -asynchronouslyWaitUntilCompleted:", ^{
+ RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0];
+
+ __block BOOL scheduledBlockRan = NO;
+ [RACScheduler.mainThreadScheduler schedule:^{
+ scheduledBlockRan = YES;
+ }];
+
+ expect(scheduledBlockRan).to.beFalsy();
+
+ NSError *error = nil;
+ BOOL success = [signal asynchronouslyWaitUntilCompleted:&error];
+
+ expect(scheduledBlockRan).to.beTruthy();
+
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ });
+});
+
+describe(@"continuation", ^{
+ it(@"should repeat after completion", ^{
+ __block NSUInteger numberOfSubscriptions = 0;
+ RACScheduler *scheduler = [RACScheduler scheduler];
+
+ RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ return [scheduler schedule:^{
+ if (numberOfSubscriptions == 3) {
+ [subscriber sendError:RACSignalTestError];
+ return;
+ }
+
+ numberOfSubscriptions++;
+
+ [subscriber sendNext:@"1"];
+ [subscriber sendCompleted];
+ [subscriber sendError:RACSignalTestError];
+ }];
+ }];
+
+ __block NSUInteger nextCount = 0;
+ __block BOOL gotCompleted = NO;
+ [[signal repeat] subscribeNext:^(id x) {
+ nextCount++;
+ } error:^(NSError *error) {
+
+ } completed:^{
+ gotCompleted = YES;
+ }];
+
+ expect(nextCount).will.equal(3);
+ expect(gotCompleted).to.beFalsy();
+ });
+
+ it(@"should stop repeating when disposed", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ NSMutableArray *values = [NSMutableArray array];
+
+ __block BOOL completed = NO;
+ __block RACDisposable *disposable = [[signal repeat] subscribeNext:^(id x) {
+ [values addObject:x];
+ [disposable dispose];
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(values).will.equal(@[ @1 ]);
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should stop repeating when disposed by -take:", ^{
+ RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ NSMutableArray *values = [NSMutableArray array];
+
+ __block BOOL completed = NO;
+ [[[signal repeat] take:1] subscribeNext:^(id x) {
+ [values addObject:x];
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(values).will.equal(@[ @1 ]);
+ expect(completed).to.beTruthy();
+ });
+});
+
+describe(@"+combineLatestWith:", ^{
+ __block RACSubject *subject1 = nil;
+ __block RACSubject *subject2 = nil;
+ __block RACSignal *combined = nil;
+
+ beforeEach(^{
+ subject1 = [RACSubject subject];
+ subject2 = [RACSubject subject];
+ combined = [RACSignal combineLatest:@[ subject1, subject2 ]];
+ });
+
+ it(@"should send next only once both signals send next", ^{
+ __block RACTuple *tuple;
+
+ [combined subscribeNext:^(id x) {
+ tuple = x;
+ }];
+
+ expect(tuple).to.beNil();
+
+ [subject1 sendNext:@"1"];
+ expect(tuple).to.beNil();
+
+ [subject2 sendNext:@"2"];
+ expect(tuple).to.equal(RACTuplePack(@"1", @"2"));
+ });
+
+ it(@"should send nexts when either signal sends multiple times", ^{
+ NSMutableArray *results = [NSMutableArray array];
+ [combined subscribeNext:^(id x) {
+ [results addObject:x];
+ }];
+
+ [subject1 sendNext:@"1"];
+ [subject2 sendNext:@"2"];
+
+ [subject1 sendNext:@"3"];
+ [subject2 sendNext:@"4"];
+
+ expect(results[0]).to.equal(RACTuplePack(@"1", @"2"));
+ expect(results[1]).to.equal(RACTuplePack(@"3", @"2"));
+ expect(results[2]).to.equal(RACTuplePack(@"3", @"4"));
+ });
+
+ it(@"should complete when only both signals complete", ^{
+ __block BOOL completed = NO;
+
+ [combined subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(completed).to.beFalsy();
+
+ [subject1 sendCompleted];
+ expect(completed).to.beFalsy();
+
+ [subject2 sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should error when either signal errors", ^{
+ __block NSError *receivedError = nil;
+ [combined subscribeError:^(NSError *error) {
+ receivedError = error;
+ }];
+
+ [subject1 sendError:RACSignalTestError];
+ expect(receivedError).to.equal(RACSignalTestError);
+ });
+
+ it(@"shouldn't create a retain cycle", ^{
+ __block BOOL subjectDeallocd = NO;
+ __block BOOL signalDeallocd = NO;
+
+ @autoreleasepool {
+ RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject];
+ [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ subjectDeallocd = YES;
+ }]];
+
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal combineLatest:@[ subject ]];
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ signalDeallocd = YES;
+ }]];
+
+ [signal subscribeCompleted:^{}];
+ [subject sendCompleted];
+ }
+
+ expect(subjectDeallocd).will.beTruthy();
+ expect(signalDeallocd).will.beTruthy();
+ });
+
+ it(@"should combine the same signal", ^{
+ RACSignal *combined = [subject1 combineLatestWith:subject1];
+
+ __block RACTuple *tuple;
+ [combined subscribeNext:^(id x) {
+ tuple = x;
+ }];
+
+ [subject1 sendNext:@"foo"];
+ expect(tuple).to.equal(RACTuplePack(@"foo", @"foo"));
+
+ [subject1 sendNext:@"bar"];
+ expect(tuple).to.equal(RACTuplePack(@"bar", @"bar"));
+ });
+
+ it(@"should combine the same side-effecting signal", ^{
+ __block NSUInteger counter = 0;
+ RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@(++counter)];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ RACSignal *combined = [sideEffectingSignal combineLatestWith:sideEffectingSignal];
+ expect(counter).to.equal(0);
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ [combined subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ expect(counter).to.equal(2);
+
+ NSArray *expected = @[ RACTuplePack(@1, @2) ];
+ expect(receivedValues).to.equal(expected);
+ });
+});
+
+describe(@"+combineLatest:", ^{
+ it(@"should return tuples even when only combining one signal", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ __block RACTuple *tuple;
+ [[RACSignal combineLatest:@[ subject ]] subscribeNext:^(id x) {
+ tuple = x;
+ }];
+
+ [subject sendNext:@"foo"];
+ expect(tuple).to.equal(RACTuplePack(@"foo"));
+ });
+
+ it(@"should complete immediately when not given any signals", ^{
+ RACSignal *signal = [RACSignal combineLatest:@[]];
+
+ __block BOOL completed = NO;
+ [signal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should only complete after all its signals complete", ^{
+ RACSubject *subject1 = [RACSubject subject];
+ RACSubject *subject2 = [RACSubject subject];
+ RACSubject *subject3 = [RACSubject subject];
+ RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ]];
+
+ __block BOOL completed = NO;
+ [combined subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(completed).to.beFalsy();
+
+ [subject1 sendCompleted];
+ expect(completed).to.beFalsy();
+
+ [subject2 sendCompleted];
+ expect(completed).to.beFalsy();
+
+ [subject3 sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+});
+
+describe(@"+combineLatest:reduce:", ^{
+ __block RACSubject *subject1;
+ __block RACSubject *subject2;
+ __block RACSubject *subject3;
+
+ beforeEach(^{
+ subject1 = [RACSubject subject];
+ subject2 = [RACSubject subject];
+ subject3 = [RACSubject subject];
+ });
+
+ it(@"should send nils for nil values", ^{
+ __block id receivedVal1;
+ __block id receivedVal2;
+ __block id receivedVal3;
+
+ RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^ id (id val1, id val2, id val3) {
+ receivedVal1 = val1;
+ receivedVal2 = val2;
+ receivedVal3 = val3;
+ return nil;
+ }];
+
+ __block BOOL gotValue = NO;
+ [combined subscribeNext:^(id x) {
+ gotValue = YES;
+ }];
+
+ [subject1 sendNext:nil];
+ [subject2 sendNext:nil];
+ [subject3 sendNext:nil];
+
+ expect(gotValue).to.beTruthy();
+ expect(receivedVal1).to.beNil();
+ expect(receivedVal2).to.beNil();
+ expect(receivedVal3).to.beNil();
+ });
+
+ it(@"should send the return result of the reduce block", ^{
+ RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3) {
+ return [NSString stringWithFormat:@"%@: %@%@", string1, string2, string3];
+ }];
+
+ __block id received;
+ [combined subscribeNext:^(id x) {
+ received = x;
+ }];
+
+ [subject1 sendNext:@"hello"];
+ [subject2 sendNext:@"world"];
+ [subject3 sendNext:@"!!1"];
+
+ expect(received).to.equal(@"hello: world!!1");
+ });
+
+ it(@"should handle multiples of the same signals", ^{
+ RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject1, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3, NSString *string4) {
+ return [NSString stringWithFormat:@"%@ : %@ = %@ : %@", string1, string2, string3, string4];
+ }];
+
+ NSMutableArray *receivedValues = NSMutableArray.array;
+
+ [combined subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ [subject1 sendNext:@"apples"];
+ expect(receivedValues.lastObject).to.beNil();
+
+ [subject2 sendNext:@"oranges"];
+ expect(receivedValues.lastObject).to.beNil();
+
+ [subject3 sendNext:@"cattle"];
+ expect(receivedValues.lastObject).to.equal(@"apples : oranges = apples : cattle");
+
+ [subject1 sendNext:@"horses"];
+ expect(receivedValues.lastObject).to.equal(@"horses : oranges = horses : cattle");
+ });
+
+ it(@"should handle multiples of the same side-effecting signal", ^{
+ __block NSUInteger counter = 0;
+ RACSignal *sideEffectingSignal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@(++counter)];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ RACSignal *combined = [RACSignal combineLatest:@[ sideEffectingSignal, sideEffectingSignal, sideEffectingSignal ] reduce:^(id x, id y, id z) {
+ return [NSString stringWithFormat:@"%@%@%@", x, y, z];
+ }];
+
+ NSMutableArray *receivedValues = [NSMutableArray array];
+ expect(counter).to.equal(0);
+
+ [combined subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ expect(counter).to.equal(3);
+ expect(receivedValues).to.equal(@[ @"123" ]);
+ });
+});
+
+describe(@"distinctUntilChanged", ^{
+ it(@"should only send values that are distinct from the previous value", ^{
+ RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ [subscriber sendNext:@2];
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }] distinctUntilChanged];
+
+ NSArray *values = sub.toArray;
+ NSArray *expected = @[ @1, @2, @1 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"shouldn't consider nils to always be distinct", ^{
+ RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:nil];
+ [subscriber sendNext:nil];
+ [subscriber sendNext:nil];
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }] distinctUntilChanged];
+
+ NSArray *values = sub.toArray;
+ NSArray *expected = @[ @1, [NSNull null], @1 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should consider initial nil to be distinct", ^{
+ RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:nil];
+ [subscriber sendNext:nil];
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }] distinctUntilChanged];
+
+ NSArray *values = sub.toArray;
+ NSArray *expected = @[ [NSNull null], @1 ];
+ expect(values).to.equal(expected);
+ });
+});
+
+describe(@"RACObserve", ^{
+ __block RACTestObject *testObject;
+
+ beforeEach(^{
+ testObject = [[RACTestObject alloc] init];
+ });
+
+ it(@"should work with object properties", ^{
+ NSArray *expected = @[ @"hello", @"world" ];
+ testObject.objectValue = expected[0];
+
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ [RACObserve(testObject, objectValue) subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ }];
+
+ testObject.objectValue = expected[1];
+
+ expect(valuesReceived).to.equal(expected);
+ });
+
+ it(@"should work with non-object properties", ^{
+ NSArray *expected = @[ @42, @43 ];
+ testObject.integerValue = [expected[0] integerValue];
+
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ [RACObserve(testObject, integerValue) subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ }];
+
+ testObject.integerValue = [expected[1] integerValue];
+
+ expect(valuesReceived).to.equal(expected);
+ });
+
+ it(@"should read the initial value upon subscription", ^{
+ testObject.objectValue = @"foo";
+
+ RACSignal *signal = RACObserve(testObject, objectValue);
+ testObject.objectValue = @"bar";
+
+ expect([signal first]).to.equal(@"bar");
+ });
+});
+
+describe(@"-setKeyPath:onObject:", ^{
+ id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) {
+ [signal setKeyPath:keyPath onObject:testObject nilValue:nilValue];
+ };
+
+ itShouldBehaveLike(RACPropertySignalExamples, ^{
+ return @{ RACPropertySignalExamplesSetupBlock: setupBlock };
+ });
+
+ it(@"shouldn't send values to dealloc'd objects", ^{
+ RACSubject *subject = [RACSubject subject];
+ @autoreleasepool {
+ RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
+ [subject setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
+ expect(testObject.objectValue).to.beNil();
+
+ [subject sendNext:@1];
+ expect(testObject.objectValue).to.equal(@1);
+
+ [subject sendNext:@2];
+ expect(testObject.objectValue).to.equal(@2);
+ }
+
+ // This shouldn't do anything.
+ [subject sendNext:@3];
+ });
+
+ it(@"should allow a new derivation after the signal's completed", ^{
+ RACSubject *subject1 = [RACSubject subject];
+ RACTestObject *testObject = [[RACTestObject alloc] init];
+ [subject1 setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
+ [subject1 sendCompleted];
+
+ RACSubject *subject2 = [RACSubject subject];
+ // This will assert if the previous completion didn't dispose of the
+ // subscription.
+ [subject2 setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
+ });
+
+ it(@"should set the given value when nil is received", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACTestObject *testObject = [[RACTestObject alloc] init];
+ [subject setKeyPath:@keypath(testObject.integerValue) onObject:testObject nilValue:@5];
+
+ [subject sendNext:@1];
+ expect(testObject.integerValue).to.equal(1);
+
+ [subject sendNext:nil];
+ expect(testObject.integerValue).to.equal(5);
+
+ [subject sendCompleted];
+ expect(testObject.integerValue).to.equal(5);
+ });
+
+ it(@"should keep object alive over -sendNext:", ^{
+ RACSubject *subject = [RACSubject subject];
+ __block RACTestObject *testObject = [[RACTestObject alloc] init];
+ __block id deallocValue;
+
+ __unsafe_unretained RACTestObject *unsafeTestObject = testObject;
+ [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocValue = unsafeTestObject.slowObjectValue;
+ }]];
+
+ [subject setKeyPath:@keypath(testObject.slowObjectValue) onObject:testObject];
+ expect(testObject.slowObjectValue).to.beNil();
+
+ // Attempt to deallocate concurrently.
+ [[RACScheduler scheduler] afterDelay:0.01 schedule:^{
+ testObject = nil;
+ }];
+
+ expect(deallocValue).to.beNil();
+ [subject sendNext:@1];
+ expect(deallocValue).to.equal(@1);
+ });
+});
+
+describe(@"memory management", ^{
+ it(@"should dealloc signals if the signal does nothing", ^{
+ __block BOOL deallocd = NO;
+ @autoreleasepool {
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+ }
+
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should retain signals for a single run loop iteration", ^{
+ __block BOOL deallocd = NO;
+
+ @autoreleasepool {
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+ }
+
+ expect(deallocd).to.beFalsy();
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should dealloc signals if the signal immediately completes", ^{
+ __block BOOL deallocd = NO;
+ @autoreleasepool {
+ __block BOOL done = NO;
+
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ [signal subscribeCompleted:^{
+ done = YES;
+ }];
+
+ expect(done).will.beTruthy();
+ }
+
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should dealloc a replay subject if it completes immediately", ^{
+ __block BOOL completed = NO;
+ __block BOOL deallocd = NO;
+ @autoreleasepool {
+ RACReplaySubject *subject __attribute__((objc_precise_lifetime)) = [RACReplaySubject subject];
+ [subject sendCompleted];
+
+ [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ [subject subscribeCompleted:^{
+ completed = YES;
+ }];
+ }
+
+ expect(completed).will.beTruthy();
+
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should dealloc if the signal was created on a background queue", ^{
+ __block BOOL completed = NO;
+ __block BOOL deallocd = NO;
+ @autoreleasepool {
+ [[RACScheduler scheduler] schedule:^{
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ [signal subscribeCompleted:^{
+ completed = YES;
+ }];
+ }];
+ }
+
+ expect(completed).will.beTruthy();
+
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should dealloc if the signal was created on a background queue, never gets any subscribers, and the background queue gets delayed", ^{
+ __block BOOL deallocd = NO;
+ @autoreleasepool {
+ [[RACScheduler scheduler] schedule:^{
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ [NSThread sleepForTimeInterval:1];
+
+ expect(deallocd).to.beFalsy();
+ }];
+ }
+
+ // The default test timeout is 1s so we'd race to see if the queue delay
+ // or default timeout happens first. To avoid that, just bump the
+ // timeout slightly for this test.
+ NSTimeInterval originalTestTimeout = Expecta.asynchronousTestTimeout;
+ Expecta.asynchronousTestTimeout = 1.1f;
+ expect(deallocd).will.beTruthy();
+ Expecta.asynchronousTestTimeout = originalTestTimeout;
+ });
+
+ it(@"should retain signals when subscribing", ^{
+ __block BOOL deallocd = NO;
+
+ RACDisposable *disposable;
+ @autoreleasepool {
+ @autoreleasepool {
+ @autoreleasepool {
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ return nil;
+ }];
+
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocd = YES;
+ }]];
+
+ disposable = [signal subscribeCompleted:^{}];
+ }
+
+ // Spin the run loop to account for RAC magic that retains the
+ // signal for a single iteration.
+ [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
+ }
+
+ expect(deallocd).to.beFalsy();
+
+ [disposable dispose];
+ }
+ expect(deallocd).will.beTruthy();
+ });
+
+ it(@"should retain intermediate signals when subscribing", ^{
+ RACSubject *subject = [RACSubject subject];
+ expect(subject).notTo.beNil();
+
+ __block BOOL gotNext = NO;
+ __block BOOL completed = NO;
+
+ RACDisposable *disposable;
+
+ @autoreleasepool {
+ @autoreleasepool {
+ RACSignal *intermediateSignal = [subject doNext:^(id _) {
+ gotNext = YES;
+ }];
+
+ expect(intermediateSignal).notTo.beNil();
+
+ disposable = [intermediateSignal subscribeCompleted:^{
+ completed = YES;
+ }];
+ }
+
+ // Spin the run loop to account for RAC magic that retains the
+ // signal for a single iteration.
+ [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
+ }
+
+ [subject sendNext:@5];
+ expect(gotNext).to.beTruthy();
+
+ [subject sendCompleted];
+ expect(completed).to.beTruthy();
+
+ [disposable dispose];
+ });
+});
+
+describe(@"-merge:", ^{
+ __block RACSubject *sub1;
+ __block RACSubject *sub2;
+ __block RACSignal *merged;
+ beforeEach(^{
+ sub1 = [RACSubject subject];
+ sub2 = [RACSubject subject];
+ merged = [sub1 merge:sub2];
+ });
+
+ it(@"should send all values from both signals", ^{
+ NSMutableArray *values = [NSMutableArray array];
+ [merged subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ [sub1 sendNext:@1];
+ [sub2 sendNext:@2];
+ [sub2 sendNext:@3];
+ [sub1 sendNext:@4];
+
+ NSArray *expected = @[ @1, @2, @3, @4 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should send an error if one occurs", ^{
+ __block NSError *errorReceived;
+ [merged subscribeError:^(NSError *error) {
+ errorReceived = error;
+ }];
+
+ [sub1 sendError:RACSignalTestError];
+ expect(errorReceived).to.equal(RACSignalTestError);
+ });
+
+ it(@"should complete only after both signals complete", ^{
+ NSMutableArray *values = [NSMutableArray array];
+ __block BOOL completed = NO;
+ [merged subscribeNext:^(id x) {
+ [values addObject:x];
+ } completed:^{
+ completed = YES;
+ }];
+
+ [sub1 sendNext:@1];
+ [sub2 sendNext:@2];
+ [sub2 sendNext:@3];
+ [sub2 sendCompleted];
+ expect(completed).to.beFalsy();
+
+ [sub1 sendNext:@4];
+ [sub1 sendCompleted];
+ expect(completed).to.beTruthy();
+
+ NSArray *expected = @[ @1, @2, @3, @4 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should complete only after both signals complete for any number of subscribers", ^{
+ __block BOOL completed1 = NO;
+ __block BOOL completed2 = NO;
+ [merged subscribeCompleted:^{
+ completed1 = YES;
+ }];
+
+ [merged subscribeCompleted:^{
+ completed2 = YES;
+ }];
+
+ expect(completed1).to.beFalsy();
+ expect(completed2).to.beFalsy();
+
+ [sub1 sendCompleted];
+ [sub2 sendCompleted];
+ expect(completed1).to.beTruthy();
+ expect(completed2).to.beTruthy();
+ });
+});
+
+describe(@"+merge:", ^{
+ __block RACSubject *sub1;
+ __block RACSubject *sub2;
+ __block RACSignal *merged;
+ beforeEach(^{
+ sub1 = [RACSubject subject];
+ sub2 = [RACSubject subject];
+ merged = [RACSignal merge:@[ sub1, sub2 ].objectEnumerator];
+ });
+
+ it(@"should send all values from both signals", ^{
+ NSMutableArray *values = [NSMutableArray array];
+ [merged subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ [sub1 sendNext:@1];
+ [sub2 sendNext:@2];
+ [sub2 sendNext:@3];
+ [sub1 sendNext:@4];
+
+ NSArray *expected = @[ @1, @2, @3, @4 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should send an error if one occurs", ^{
+ __block NSError *errorReceived;
+ [merged subscribeError:^(NSError *error) {
+ errorReceived = error;
+ }];
+
+ [sub1 sendError:RACSignalTestError];
+ expect(errorReceived).to.equal(RACSignalTestError);
+ });
+
+ it(@"should complete only after both signals complete", ^{
+ NSMutableArray *values = [NSMutableArray array];
+ __block BOOL completed = NO;
+ [merged subscribeNext:^(id x) {
+ [values addObject:x];
+ } completed:^{
+ completed = YES;
+ }];
+
+ [sub1 sendNext:@1];
+ [sub2 sendNext:@2];
+ [sub2 sendNext:@3];
+ [sub2 sendCompleted];
+ expect(completed).to.beFalsy();
+
+ [sub1 sendNext:@4];
+ [sub1 sendCompleted];
+ expect(completed).to.beTruthy();
+
+ NSArray *expected = @[ @1, @2, @3, @4 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should complete immediately when not given any signals", ^{
+ RACSignal *signal = [RACSignal merge:@[].objectEnumerator];
+
+ __block BOOL completed = NO;
+ [signal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should complete only after both signals complete for any number of subscribers", ^{
+ __block BOOL completed1 = NO;
+ __block BOOL completed2 = NO;
+ [merged subscribeCompleted:^{
+ completed1 = YES;
+ }];
+
+ [merged subscribeCompleted:^{
+ completed2 = YES;
+ }];
+
+ expect(completed1).to.beFalsy();
+ expect(completed2).to.beFalsy();
+
+ [sub1 sendCompleted];
+ [sub2 sendCompleted];
+ expect(completed1).to.beTruthy();
+ expect(completed2).to.beTruthy();
+ });
+});
+
+describe(@"-flatten:", ^{
+ __block BOOL subscribedTo1 = NO;
+ __block BOOL subscribedTo2 = NO;
+ __block BOOL subscribedTo3 = NO;
+ __block RACSignal *sub1;
+ __block RACSignal *sub2;
+ __block RACSignal *sub3;
+ __block RACSubject *subject1;
+ __block RACSubject *subject2;
+ __block RACSubject *subject3;
+ __block RACSubject *signalsSubject;
+ __block NSMutableArray *values;
+
+ beforeEach(^{
+ subscribedTo1 = NO;
+ subject1 = [RACSubject subject];
+ sub1 = [RACSignal defer:^{
+ subscribedTo1 = YES;
+ return subject1;
+ }];
+
+ subscribedTo2 = NO;
+ subject2 = [RACSubject subject];
+ sub2 = [RACSignal defer:^{
+ subscribedTo2 = YES;
+ return subject2;
+ }];
+
+ subscribedTo3 = NO;
+ subject3 = [RACSubject subject];
+ sub3 = [RACSignal defer:^{
+ subscribedTo3 = YES;
+ return subject3;
+ }];
+
+ signalsSubject = [RACSubject subject];
+
+ values = [NSMutableArray array];
+ });
+
+ describe(@"when its max is 0", ^{
+ it(@"should merge all the signals concurrently", ^{
+ [[signalsSubject flatten:0] subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ expect(subscribedTo1).to.beFalsy();
+ expect(subscribedTo2).to.beFalsy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [signalsSubject sendNext:sub1];
+ [signalsSubject sendNext:sub2];
+
+ expect(subscribedTo1).to.beTruthy();
+ expect(subscribedTo2).to.beTruthy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [subject1 sendNext:@1];
+
+ [signalsSubject sendNext:sub3];
+
+ expect(subscribedTo1).to.beTruthy();
+ expect(subscribedTo2).to.beTruthy();
+ expect(subscribedTo3).to.beTruthy();
+
+ [subject1 sendCompleted];
+
+ [subject2 sendNext:@2];
+ [subject2 sendCompleted];
+
+ [subject3 sendNext:@3];
+ [subject3 sendCompleted];
+
+ NSArray *expected = @[ @1, @2, @3 ];
+ expect(values).to.equal(expected);
+ });
+
+ itShouldBehaveLike(RACSignalMergeConcurrentCompletionExampleGroup, @{ RACSignalMaxConcurrent: @0 });
+ });
+
+ describe(@"when its max is > 0", ^{
+ it(@"should merge only the given number at a time", ^{
+ [[signalsSubject flatten:1] subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ expect(subscribedTo1).to.beFalsy();
+ expect(subscribedTo2).to.beFalsy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [signalsSubject sendNext:sub1];
+ [signalsSubject sendNext:sub2];
+
+ expect(subscribedTo1).to.beTruthy();
+ expect(subscribedTo2).to.beFalsy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [subject1 sendNext:@1];
+
+ [signalsSubject sendNext:sub3];
+
+ expect(subscribedTo1).to.beTruthy();
+ expect(subscribedTo2).to.beFalsy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [signalsSubject sendCompleted];
+
+ expect(subscribedTo1).to.beTruthy();
+ expect(subscribedTo2).to.beFalsy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [subject1 sendCompleted];
+
+ expect(subscribedTo2).to.beTruthy();
+ expect(subscribedTo3).to.beFalsy();
+
+ [subject2 sendNext:@2];
+ [subject2 sendCompleted];
+
+ expect(subscribedTo3).to.beTruthy();
+
+ [subject3 sendNext:@3];
+ [subject3 sendCompleted];
+
+ NSArray *expected = @[ @1, @2, @3 ];
+ expect(values).to.equal(expected);
+ });
+
+ itShouldBehaveLike(RACSignalMergeConcurrentCompletionExampleGroup, @{ RACSignalMaxConcurrent: @1 });
+ });
+
+ it(@"shouldn't create a retain cycle", ^{
+ __block BOOL subjectDeallocd = NO;
+ __block BOOL signalDeallocd = NO;
+ @autoreleasepool {
+ RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject];
+ [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ subjectDeallocd = YES;
+ }]];
+
+ RACSignal *signal __attribute__((objc_precise_lifetime)) = [subject flatten];
+ [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ signalDeallocd = YES;
+ }]];
+
+ [signal subscribeCompleted:^{}];
+
+ [subject sendCompleted];
+ }
+
+ expect(subjectDeallocd).will.beTruthy();
+ expect(signalDeallocd).will.beTruthy();
+ });
+
+ it(@"should not crash when disposing while subscribing", ^{
+ RACDisposable *disposable = [[signalsSubject flatten:0] subscribeCompleted:^{
+ }];
+
+ [signalsSubject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [disposable dispose];
+ [subscriber sendCompleted];
+ return nil;
+ }]];
+
+ [signalsSubject sendCompleted];
+ });
+
+ it(@"should dispose after last synchronous signal subscription and should not crash", ^{
+
+ RACSignal *flattened = [signalsSubject flatten:1];
+ RACDisposable *flattenDisposable = [flattened subscribeCompleted:^{}];
+
+ RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) {
+ expect(flattenDisposable.disposed).to.beFalsy();
+ [subscriber sendCompleted];
+ expect(flattenDisposable.disposed).to.beTruthy();
+ return nil;
+ }];
+
+ RACSignal *asyncSignal = [sub1 delay:0];
+
+ [signalsSubject sendNext:asyncSignal];
+ [signalsSubject sendNext:syncSignal];
+
+ [signalsSubject sendCompleted];
+
+ [subject1 sendCompleted];
+
+ expect(flattenDisposable.disposed).will.beTruthy();
+ });
+
+ it(@"should not crash when disposed because of takeUntil:", ^{
+ for (int i = 0; i < 100; i++) {
+ RACSubject *flattenedReceiver = [RACSubject subject];
+ RACSignal *done = [flattenedReceiver map:^(NSNumber *n) {
+ return @(n.integerValue == 1);
+ }];
+
+ RACSignal *flattened = [signalsSubject flatten:1];
+
+ RACDisposable *flattenDisposable = [[flattened takeUntil:[done ignore:@NO]] subscribe:flattenedReceiver];
+
+ RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) {
+ expect(flattenDisposable.disposed).to.beFalsy();
+ [subscriber sendNext:@1];
+ expect(flattenDisposable.disposed).to.beTruthy();
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ RACSignal *asyncSignal = [sub1 delay:0];
+ [subject1 sendNext:@0];
+
+ [signalsSubject sendNext:asyncSignal];
+ [signalsSubject sendNext:syncSignal];
+ [signalsSubject sendCompleted];
+
+ [subject1 sendCompleted];
+
+ expect(flattenDisposable.disposed).will.beTruthy();
+ }
+ });
+});
+
+describe(@"-switchToLatest", ^{
+ __block RACSubject *subject;
+
+ __block NSMutableArray *values;
+ __block NSError *lastError = nil;
+ __block BOOL completed = NO;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+
+ values = [NSMutableArray array];
+ lastError = nil;
+ completed = NO;
+
+ [[subject switchToLatest] subscribeNext:^(id x) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [values addObject:x];
+ } error:^(NSError *error) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ lastError = error;
+ } completed:^{
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ completed = YES;
+ }];
+ });
+
+ it(@"should send values from the most recent signal", ^{
+ [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ return nil;
+ }]];
+
+ [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@3];
+ [subscriber sendNext:@4];
+ return nil;
+ }]];
+
+ NSArray *expected = @[ @1, @2, @3, @4 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should send errors from the most recent signal", ^{
+ [subject sendNext:[RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ return nil;
+ }]];
+
+ expect(lastError).notTo.beNil();
+ });
+
+ it(@"should not send completed if only the switching signal completes", ^{
+ [subject sendNext:RACSignal.never];
+
+ expect(completed).to.beFalsy();
+
+ [subject sendCompleted];
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should send completed when the switching signal completes and the last sent signal does", ^{
+ [subject sendNext:RACSignal.empty];
+
+ expect(completed).to.beFalsy();
+
+ [subject sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should accept nil signals", ^{
+ [subject sendNext:nil];
+ [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ return nil;
+ }]];
+
+ NSArray *expected = @[ @1, @2 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should return a cold signal", ^{
+ __block NSUInteger subscriptions = 0;
+ RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ subscriptions++;
+ [subscriber sendNext:[RACSignal empty]];
+ return nil;
+ }];
+
+ RACSignal *switched = [signalOfSignals switchToLatest];
+
+ [[switched publish] connect];
+ expect(subscriptions).to.equal(1);
+
+ [[switched publish] connect];
+ expect(subscriptions).to.equal(2);
+ });
+});
+
+describe(@"+switch:cases:default:", ^{
+ __block RACSubject *keySubject;
+
+ __block RACSubject *subjectZero;
+ __block RACSubject *subjectOne;
+ __block RACSubject *subjectTwo;
+
+ __block RACSubject *defaultSubject;
+
+ __block NSMutableArray *values;
+ __block NSError *lastError = nil;
+ __block BOOL completed = NO;
+
+ beforeEach(^{
+ keySubject = [RACSubject subject];
+
+ subjectZero = [RACSubject subject];
+ subjectOne = [RACSubject subject];
+ subjectTwo = [RACSubject subject];
+
+ defaultSubject = [RACSubject subject];
+
+ values = [NSMutableArray array];
+ lastError = nil;
+ completed = NO;
+ });
+
+ describe(@"switching between values with a default", ^{
+ __block RACSignal *switchSignal;
+
+ beforeEach(^{
+ switchSignal = [RACSignal switch:keySubject cases:@{
+ @0: subjectZero,
+ @1: subjectOne,
+ @2: subjectTwo,
+ } default:[RACSignal never]];
+
+ [switchSignal subscribeNext:^(id x) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [values addObject:x];
+ } error:^(NSError *error) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ lastError = error;
+ } completed:^{
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ completed = YES;
+ }];
+ });
+
+ it(@"should not send any values before a key is sent", ^{
+ [subjectZero sendNext:RACUnit.defaultUnit];
+ [subjectOne sendNext:RACUnit.defaultUnit];
+ [subjectTwo sendNext:RACUnit.defaultUnit];
+
+ expect(values).to.equal(@[]);
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should send events based on the latest key", ^{
+ [keySubject sendNext:@0];
+
+ [subjectZero sendNext:@"zero"];
+ [subjectZero sendNext:@"zero"];
+ [subjectOne sendNext:@"one"];
+ [subjectTwo sendNext:@"two"];
+
+ NSArray *expected = @[ @"zero", @"zero" ];
+ expect(values).to.equal(expected);
+
+ [keySubject sendNext:@1];
+
+ [subjectZero sendNext:@"zero"];
+ [subjectOne sendNext:@"one"];
+ [subjectTwo sendNext:@"two"];
+
+ expected = @[ @"zero", @"zero", @"one" ];
+ expect(values).to.equal(expected);
+
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [keySubject sendNext:@2];
+
+ [subjectZero sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ [subjectOne sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ expect(lastError).to.beNil();
+
+ [subjectTwo sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ expect(lastError).notTo.beNil();
+ });
+
+ it(@"should not send completed when only the key signal completes", ^{
+ [keySubject sendNext:@0];
+ [subjectZero sendNext:@"zero"];
+ [keySubject sendCompleted];
+
+ expect(values).to.equal(@[ @"zero" ]);
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should send completed when the key signal and the latest sent signal complete", ^{
+ [keySubject sendNext:@0];
+ [subjectZero sendNext:@"zero"];
+ [keySubject sendCompleted];
+ [subjectZero sendCompleted];
+
+ expect(values).to.equal(@[ @"zero" ]);
+ expect(completed).to.beTruthy();
+ });
+ });
+
+ it(@"should use the default signal if key that was sent does not have an associated signal", ^{
+ [[RACSignal
+ switch:keySubject
+ cases:@{
+ @0: subjectZero,
+ @1: subjectOne,
+ }
+ default:defaultSubject]
+ subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ [keySubject sendNext:@"not a valid key"];
+ [defaultSubject sendNext:@"default"];
+
+ expect(values).to.equal(@[ @"default" ]);
+
+ [keySubject sendNext:nil];
+ [defaultSubject sendNext:@"default"];
+
+ expect(values).to.equal((@[ @"default", @"default" ]));
+ });
+
+ it(@"should send an error if key that was sent does not have an associated signal and there's no default", ^{
+ [[RACSignal
+ switch:keySubject
+ cases:@{
+ @0: subjectZero,
+ @1: subjectOne,
+ }
+ default:nil]
+ subscribeNext:^(id x) {
+ [values addObject:x];
+ } error:^(NSError *error) {
+ lastError = error;
+ }];
+
+ [keySubject sendNext:@0];
+ [subjectZero sendNext:@"zero"];
+
+ expect(values).to.equal(@[ @"zero" ]);
+ expect(lastError).to.beNil();
+
+ [keySubject sendNext:nil];
+
+ expect(values).to.equal(@[ @"zero" ]);
+ expect(lastError).notTo.beNil();
+ expect(lastError.domain).to.equal(RACSignalErrorDomain);
+ expect(lastError.code).to.equal(RACSignalErrorNoMatchingCase);
+ });
+
+ it(@"should match RACTupleNil case when a nil value is sent", ^{
+ [[RACSignal
+ switch:keySubject
+ cases:@{
+ RACTupleNil.tupleNil: subjectZero,
+ }
+ default:defaultSubject]
+ subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ [keySubject sendNext:nil];
+ [subjectZero sendNext:@"zero"];
+ expect(values).to.equal(@[ @"zero" ]);
+ });
+});
+
+describe(@"+if:then:else", ^{
+ __block RACSubject *boolSubject;
+ __block RACSubject *trueSubject;
+ __block RACSubject *falseSubject;
+
+ __block NSMutableArray *values;
+ __block NSError *lastError = nil;
+ __block BOOL completed = NO;
+
+ beforeEach(^{
+ boolSubject = [RACSubject subject];
+ trueSubject = [RACSubject subject];
+ falseSubject = [RACSubject subject];
+
+ values = [NSMutableArray array];
+ lastError = nil;
+ completed = NO;
+
+ [[RACSignal if:boolSubject then:trueSubject else:falseSubject] subscribeNext:^(id x) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [values addObject:x];
+ } error:^(NSError *error) {
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ lastError = error;
+ } completed:^{
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ completed = YES;
+ }];
+ });
+
+ it(@"should not send any values before a boolean is sent", ^{
+ [trueSubject sendNext:RACUnit.defaultUnit];
+ [falseSubject sendNext:RACUnit.defaultUnit];
+
+ expect(values).to.equal(@[]);
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should send events based on the latest boolean", ^{
+ [boolSubject sendNext:@YES];
+
+ [trueSubject sendNext:@"foo"];
+ [falseSubject sendNext:@"buzz"];
+ [trueSubject sendNext:@"bar"];
+
+ NSArray *expected = @[ @"foo", @"bar" ];
+ expect(values).to.equal(expected);
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [boolSubject sendNext:@NO];
+
+ [trueSubject sendNext:@"baz"];
+ [falseSubject sendNext:@"buzz"];
+ [trueSubject sendNext:@"barfoo"];
+
+ expected = @[ @"foo", @"bar", @"buzz" ];
+ expect(values).to.equal(expected);
+ expect(lastError).to.beNil();
+ expect(completed).to.beFalsy();
+
+ [trueSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ expect(lastError).to.beNil();
+
+ [falseSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
+ expect(lastError).notTo.beNil();
+ });
+
+ it(@"should not send completed when only the BOOL signal completes", ^{
+ [boolSubject sendNext:@YES];
+ [trueSubject sendNext:@"foo"];
+ [boolSubject sendCompleted];
+
+ expect(values).to.equal(@[ @"foo" ]);
+ expect(completed).to.beFalsy();
+ });
+
+ it(@"should send completed when the BOOL signal and the latest sent signal complete", ^{
+ [boolSubject sendNext:@YES];
+ [trueSubject sendNext:@"foo"];
+ [trueSubject sendCompleted];
+ [boolSubject sendCompleted];
+
+ expect(values).to.equal(@[ @"foo" ]);
+ expect(completed).to.beTruthy();
+ });
+});
+
+describe(@"+interval:onScheduler: and +interval:onScheduler:withLeeway:", ^{
+ static const NSTimeInterval interval = 0.1;
+ static const NSTimeInterval leeway = 0.2;
+
+ __block void (^testTimer)(RACSignal *, NSNumber *, NSNumber *) = nil;
+
+ before(^{
+ testTimer = [^(RACSignal *timer, NSNumber *minInterval, NSNumber *leeway) {
+ __block NSUInteger nextsReceived = 0;
+
+ NSTimeInterval startTime = NSDate.timeIntervalSinceReferenceDate;
+ [[timer take:3] subscribeNext:^(NSDate *date) {
+ ++nextsReceived;
+
+ NSTimeInterval currentTime = date.timeIntervalSinceReferenceDate;
+
+ // Uniformly distribute the expected interval for all
+ // received values. We do this instead of saving a timestamp
+ // because a delayed interval may cause the _next_ value to
+ // send sooner than the interval.
+ NSTimeInterval expectedMinInterval = minInterval.doubleValue * nextsReceived;
+ NSTimeInterval expectedMaxInterval = expectedMinInterval + leeway.doubleValue * 3 + 0.05;
+
+ expect(currentTime - startTime).beGreaterThanOrEqualTo(expectedMinInterval);
+ expect(currentTime - startTime).beLessThanOrEqualTo(expectedMaxInterval);
+ }];
+
+ expect(nextsReceived).will.equal(3);
+ } copy];
+ });
+
+ describe(@"+interval:onScheduler:", ^{
+ it(@"should work on the main thread scheduler", ^{
+ testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler], @(interval), @0);
+ });
+
+ it(@"should work on a background scheduler", ^{
+ testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler]], @(interval), @0);
+ });
+ });
+
+ describe(@"+interval:onScheduler:withLeeway:", ^{
+ it(@"should work on the main thread scheduler", ^{
+ testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler withLeeway:leeway], @(interval), @(leeway));
+ });
+
+ it(@"should work on a background scheduler", ^{
+ testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler] withLeeway:leeway], @(interval), @(leeway));
+ });
+ });
+});
+
+describe(@"-timeout:onScheduler:", ^{
+ __block RACSubject *subject;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ });
+
+ it(@"should time out", ^{
+ RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];
+
+ __block NSError *receivedError = nil;
+ [[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) {
+ receivedError = e;
+ }];
+
+ expect(receivedError).to.beNil();
+
+ [scheduler stepAll];
+ expect(receivedError).willNot.beNil();
+ expect(receivedError.domain).to.equal(RACSignalErrorDomain);
+ expect(receivedError.code).to.equal(RACSignalErrorTimedOut);
+ });
+
+ it(@"should pass through events while not timed out", ^{
+ __block id next = nil;
+ __block BOOL completed = NO;
+ [[subject timeout:1 onScheduler:RACScheduler.mainThreadScheduler] subscribeNext:^(id x) {
+ next = x;
+ } completed:^{
+ completed = YES;
+ }];
+
+ [subject sendNext:RACUnit.defaultUnit];
+ expect(next).to.equal(RACUnit.defaultUnit);
+
+ [subject sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should not time out after disposal", ^{
+ RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];
+
+ __block NSError *receivedError = nil;
+ RACDisposable *disposable = [[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) {
+ receivedError = e;
+ }];
+
+ [disposable dispose];
+ [scheduler stepAll];
+ expect(receivedError).to.beNil();
+ });
+});
+
+describe(@"-delay:", ^{
+ __block RACSubject *subject;
+ __block RACSignal *delayedSignal;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ delayedSignal = [subject delay:0];
+ });
+
+ it(@"should delay nexts", ^{
+ __block id next = nil;
+ [delayedSignal subscribeNext:^(id x) {
+ next = x;
+ }];
+
+ [subject sendNext:@"foo"];
+ expect(next).to.beNil();
+ expect(next).will.equal(@"foo");
+ });
+
+ it(@"should delay completed", ^{
+ __block BOOL completed = NO;
+ [delayedSignal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ [subject sendCompleted];
+ expect(completed).to.beFalsy();
+ expect(completed).will.beTruthy();
+ });
+
+ it(@"should not delay errors", ^{
+ __block NSError *error = nil;
+ [delayedSignal subscribeError:^(NSError *e) {
+ error = e;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should cancel delayed events when disposed", ^{
+ __block id next = nil;
+ RACDisposable *disposable = [delayedSignal subscribeNext:^(id x) {
+ next = x;
+ }];
+
+ [subject sendNext:@"foo"];
+
+ __block BOOL done = NO;
+ [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ done = YES;
+ }];
+
+ [disposable dispose];
+
+ expect(done).will.beTruthy();
+ expect(next).to.beNil();
+ });
+});
+
+describe(@"-catch:", ^{
+ it(@"should subscribe to ensuing signal on error", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ RACSignal *signal = [subject catch:^(NSError *error) {
+ return [RACSignal return:@41];
+ }];
+
+ __block id value = nil;
+ [signal subscribeNext:^(id x) {
+ value = x;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(value).to.equal(@41);
+ });
+
+ it(@"should prevent source error from propagating", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ RACSignal *signal = [subject catch:^(NSError *error) {
+ return [RACSignal empty];
+ }];
+
+ __block BOOL errorReceived = NO;
+ [signal subscribeError:^(NSError *error) {
+ errorReceived = YES;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(errorReceived).to.beFalsy();
+ });
+
+ it(@"should propagate error from ensuing signal", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ NSError *secondaryError = [NSError errorWithDomain:@"bubs" code:41 userInfo:nil];
+ RACSignal *signal = [subject catch:^(NSError *error) {
+ return [RACSignal error:secondaryError];
+ }];
+
+ __block NSError *errorReceived = nil;
+ [signal subscribeError:^(NSError *error) {
+ errorReceived = error;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(errorReceived).to.equal(secondaryError);
+ });
+
+ it(@"should dispose ensuing signal", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ __block BOOL disposed = NO;
+ RACSignal *signal = [subject catch:^(NSError *error) {
+ return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+ }];
+
+ RACDisposable *disposable = [signal subscribeCompleted:^{}];
+ [subject sendError:RACSignalTestError];
+ [disposable dispose];
+
+ expect(disposed).will.beTruthy();
+ });
+});
+
+describe(@"-try:", ^{
+ __block RACSubject *subject;
+ __block NSError *receivedError;
+ __block NSMutableArray *nextValues;
+ __block BOOL completed;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ nextValues = [NSMutableArray array];
+ completed = NO;
+ receivedError = nil;
+
+ [[subject try:^(NSString *value, NSError **error) {
+ if (value != nil) return YES;
+
+ if (error != nil) *error = RACSignalTestError;
+
+ return NO;
+ }] subscribeNext:^(id x) {
+ [nextValues addObject:x];
+ } error:^(NSError *error) {
+ receivedError = error;
+ } completed:^{
+ completed = YES;
+ }];
+ });
+
+ it(@"should pass values while YES is returned from the tryBlock", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ [subject sendNext:@"baz"];
+ [subject sendNext:@"buzz"];
+ [subject sendCompleted];
+
+ NSArray *receivedValues = [nextValues copy];
+ NSArray *expectedValues = @[ @"foo", @"bar", @"baz", @"buzz" ];
+
+ expect(receivedError).to.beNil();
+ expect(receivedValues).to.equal(expectedValues);
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should pass values until NO is returned from the tryBlock", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ [subject sendNext:nil];
+ [subject sendNext:@"buzz"];
+ [subject sendCompleted];
+
+ NSArray *receivedValues = [nextValues copy];
+ NSArray *expectedValues = @[ @"foo", @"bar" ];
+
+ expect(receivedError).to.equal(RACSignalTestError);
+ expect(receivedValues).to.equal(expectedValues);
+ expect(completed).to.beFalsy();
+ });
+});
+
+describe(@"-tryMap:", ^{
+ __block RACSubject *subject;
+ __block NSError *receivedError;
+ __block NSMutableArray *nextValues;
+ __block BOOL completed;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ nextValues = [NSMutableArray array];
+ completed = NO;
+ receivedError = nil;
+
+ [[subject tryMap:^ id (NSString *value, NSError **error) {
+ if (value != nil) return [NSString stringWithFormat:@"%@_a", value];
+
+ if (error != nil) *error = RACSignalTestError;
+
+ return nil;
+ }] subscribeNext:^(id x) {
+ [nextValues addObject:x];
+ } error:^(NSError *error) {
+ receivedError = error;
+ } completed:^{
+ completed = YES;
+ }];
+ });
+
+ it(@"should map values with the mapBlock", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ [subject sendNext:@"baz"];
+ [subject sendNext:@"buzz"];
+ [subject sendCompleted];
+
+ NSArray *receivedValues = [nextValues copy];
+ NSArray *expectedValues = @[ @"foo_a", @"bar_a", @"baz_a", @"buzz_a" ];
+
+ expect(receivedError).to.beNil();
+ expect(receivedValues).to.equal(expectedValues);
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should map values with the mapBlock, until the mapBlock returns nil", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ [subject sendNext:nil];
+ [subject sendNext:@"buzz"];
+ [subject sendCompleted];
+
+ NSArray *receivedValues = [nextValues copy];
+ NSArray *expectedValues = @[ @"foo_a", @"bar_a" ];
+
+ expect(receivedError).to.equal(RACSignalTestError);
+ expect(receivedValues).to.equal(expectedValues);
+ expect(completed).to.beFalsy();
+ });
+});
+
+describe(@"throttling", ^{
+ __block RACSubject *subject;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ });
+
+ describe(@"-throttle:", ^{
+ __block RACSignal *throttledSignal;
+
+ beforeEach(^{
+ throttledSignal = [subject throttle:0];
+ });
+
+ it(@"should throttle nexts", ^{
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ [throttledSignal subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ }];
+
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ expect(valuesReceived).to.equal(@[]);
+
+ NSArray *expected = @[ @"bar" ];
+ expect(valuesReceived).will.equal(expected);
+
+ [subject sendNext:@"buzz"];
+ expect(valuesReceived).to.equal(expected);
+
+ expected = @[ @"bar", @"buzz" ];
+ expect(valuesReceived).will.equal(expected);
+ });
+
+ it(@"should forward completed immediately", ^{
+ __block BOOL completed = NO;
+ [throttledSignal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ [subject sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should forward errors immediately", ^{
+ __block NSError *error = nil;
+ [throttledSignal subscribeError:^(NSError *e) {
+ error = e;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should cancel future nexts when disposed", ^{
+ __block id next = nil;
+ RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) {
+ next = x;
+ }];
+
+ [subject sendNext:@"foo"];
+
+ __block BOOL done = NO;
+ [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ done = YES;
+ }];
+
+ [disposable dispose];
+
+ expect(done).will.beTruthy();
+ expect(next).to.beNil();
+ });
+ });
+
+ describe(@"-throttle:valuesPassingTest:", ^{
+ __block RACSignal *throttledSignal;
+ __block BOOL shouldThrottle;
+
+ beforeEach(^{
+ shouldThrottle = YES;
+
+ __block id value = nil;
+ throttledSignal = [[subject
+ doNext:^(id x) {
+ value = x;
+ }]
+ throttle:0 valuesPassingTest:^(id x) {
+ // Make sure that we're given the latest value.
+ expect(x).to.beIdenticalTo(value);
+
+ return shouldThrottle;
+ }];
+
+ expect(throttledSignal).notTo.beNil();
+ });
+
+ describe(@"nexts", ^{
+ __block NSMutableArray *valuesReceived;
+ __block NSMutableArray *expected;
+
+ beforeEach(^{
+ expected = [[NSMutableArray alloc] init];
+ valuesReceived = [[NSMutableArray alloc] init];
+
+ [throttledSignal subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ }];
+ });
+
+ it(@"should forward unthrottled values immediately", ^{
+ shouldThrottle = NO;
+ [subject sendNext:@"foo"];
+
+ [expected addObject:@"foo"];
+ expect(valuesReceived).to.equal(expected);
+ });
+
+ it(@"should delay throttled values", ^{
+ [subject sendNext:@"bar"];
+ expect(valuesReceived).to.equal(expected);
+
+ [expected addObject:@"bar"];
+ expect(valuesReceived).will.equal(expected);
+ });
+
+ it(@"should drop buffered values when a throttled value arrives", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+ [subject sendNext:@"buzz"];
+ expect(valuesReceived).to.equal(expected);
+
+ [expected addObject:@"buzz"];
+ expect(valuesReceived).will.equal(expected);
+ });
+
+ it(@"should drop buffered values when an immediate value arrives", ^{
+ [subject sendNext:@"foo"];
+ [subject sendNext:@"bar"];
+
+ shouldThrottle = NO;
+ [subject sendNext:@"buzz"];
+ [expected addObject:@"buzz"];
+ expect(valuesReceived).to.equal(expected);
+
+ // Make sure that nothing weird happens when sending another
+ // throttled value.
+ shouldThrottle = YES;
+ [subject sendNext:@"baz"];
+ expect(valuesReceived).to.equal(expected);
+
+ [expected addObject:@"baz"];
+ expect(valuesReceived).will.equal(expected);
+ });
+
+ it(@"should not be resent upon completion", ^{
+ [subject sendNext:@"bar"];
+ [expected addObject:@"bar"];
+ expect(valuesReceived).will.equal(expected);
+
+ [subject sendCompleted];
+ expect(valuesReceived).to.equal(expected);
+ });
+ });
+
+ it(@"should forward completed immediately", ^{
+ __block BOOL completed = NO;
+ [throttledSignal subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ [subject sendCompleted];
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should forward errors immediately", ^{
+ __block NSError *error = nil;
+ [throttledSignal subscribeError:^(NSError *e) {
+ error = e;
+ }];
+
+ [subject sendError:RACSignalTestError];
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should cancel future nexts when disposed", ^{
+ __block id next = nil;
+ RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) {
+ next = x;
+ }];
+
+ [subject sendNext:@"foo"];
+
+ __block BOOL done = NO;
+ [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
+ done = YES;
+ }];
+
+ [disposable dispose];
+
+ expect(done).will.beTruthy();
+ expect(next).to.beNil();
+ });
+ });
+});
+
+describe(@"-then:", ^{
+ it(@"should continue onto returned signal", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ __block id value = nil;
+ [[subject then:^{
+ return [RACSignal return:@2];
+ }] subscribeNext:^(id x) {
+ value = x;
+ }];
+
+ [subject sendNext:@1];
+
+ // The value shouldn't change until the first signal completes.
+ expect(value).to.beNil();
+
+ [subject sendCompleted];
+
+ expect(value).to.equal(@2);
+ });
+
+ it(@"should sequence even if no next value is sent", ^{
+ RACSubject *subject = [RACSubject subject];
+
+ __block id value = nil;
+ [[subject then:^{
+ return [RACSignal return:RACUnit.defaultUnit];
+ }] subscribeNext:^(id x) {
+ value = x;
+ }];
+
+ [subject sendCompleted];
+
+ expect(value).to.equal(RACUnit.defaultUnit);
+ });
+});
+
+describe(@"-sequence", ^{
+ RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:@2];
+ [subscriber sendNext:@3];
+ [subscriber sendNext:@4];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ itShouldBehaveLike(RACSequenceExamples, ^{
+ return @{
+ RACSequenceExampleSequence: signal.sequence,
+ RACSequenceExampleExpectedValues: @[ @1, @2, @3, @4 ]
+ };
+ });
+});
+
+it(@"should complete take: even if the original signal doesn't", ^{
+ RACSignal *sendOne = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:RACUnit.defaultUnit];
+ return nil;
+ }];
+
+ __block id value = nil;
+ __block BOOL completed = NO;
+ [[sendOne take:1] subscribeNext:^(id received) {
+ value = received;
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(value).to.equal(RACUnit.defaultUnit);
+ expect(completed).to.beTruthy();
+});
+
+describe(@"+zip:", ^{
+ __block RACSubject *subject1 = nil;
+ __block RACSubject *subject2 = nil;
+ __block BOOL hasSentError = NO;
+ __block BOOL hasSentCompleted = NO;
+ __block RACDisposable *disposable = nil;
+ __block void (^send2NextAndErrorTo1)(void) = nil;
+ __block void (^send3NextAndErrorTo1)(void) = nil;
+ __block void (^send2NextAndCompletedTo2)(void) = nil;
+ __block void (^send3NextAndCompletedTo2)(void) = nil;
+
+ before(^{
+ send2NextAndErrorTo1 = [^{
+ [subject1 sendNext:@1];
+ [subject1 sendNext:@2];
+ [subject1 sendError:RACSignalTestError];
+ } copy];
+ send3NextAndErrorTo1 = [^{
+ [subject1 sendNext:@1];
+ [subject1 sendNext:@2];
+ [subject1 sendNext:@3];
+ [subject1 sendError:RACSignalTestError];
+ } copy];
+ send2NextAndCompletedTo2 = [^{
+ [subject2 sendNext:@1];
+ [subject2 sendNext:@2];
+ [subject2 sendCompleted];
+ } copy];
+ send3NextAndCompletedTo2 = [^{
+ [subject2 sendNext:@1];
+ [subject2 sendNext:@2];
+ [subject2 sendNext:@3];
+ [subject2 sendCompleted];
+ } copy];
+ subject1 = [RACSubject subject];
+ subject2 = [RACSubject subject];
+ hasSentError = NO;
+ hasSentCompleted = NO;
+ disposable = [[RACSignal zip:@[ subject1, subject2 ]] subscribeError:^(NSError *error) {
+ hasSentError = YES;
+ } completed:^{
+ hasSentCompleted = YES;
+ }];
+ });
+
+ after(^{
+ [disposable dispose];
+ });
+
+ it(@"should complete as soon as no new zipped values are possible", ^{
+ [subject1 sendNext:@1];
+ [subject2 sendNext:@1];
+ expect(hasSentCompleted).to.beFalsy();
+
+ [subject1 sendNext:@2];
+ [subject1 sendCompleted];
+ expect(hasSentCompleted).to.beFalsy();
+
+ [subject2 sendNext:@2];
+ expect(hasSentCompleted).to.beTruthy();
+ });
+
+ it(@"outcome should not be dependent on order of signals", ^{
+ [subject2 sendCompleted];
+ expect(hasSentCompleted).to.beTruthy();
+ });
+
+ it(@"should forward errors sent earlier than (time-wise) and before (position-wise) a complete", ^{
+ send2NextAndErrorTo1();
+ send3NextAndCompletedTo2();
+ expect(hasSentError).to.beTruthy();
+ expect(hasSentCompleted).to.beFalsy();
+ });
+
+ it(@"should forward errors sent earlier than (time-wise) and after (position-wise) a complete", ^{
+ send3NextAndErrorTo1();
+ send2NextAndCompletedTo2();
+ expect(hasSentError).to.beTruthy();
+ expect(hasSentCompleted).to.beFalsy();
+ });
+
+ it(@"should forward errors sent later than (time-wise) and before (position-wise) a complete", ^{
+ send3NextAndCompletedTo2();
+ send2NextAndErrorTo1();
+ expect(hasSentError).to.beTruthy();
+ expect(hasSentCompleted).to.beFalsy();
+ });
+
+ it(@"should ignore errors sent later than (time-wise) and after (position-wise) a complete", ^{
+ send2NextAndCompletedTo2();
+ send3NextAndErrorTo1();
+ expect(hasSentError).to.beFalsy();
+ expect(hasSentCompleted).to.beTruthy();
+ });
+
+ it(@"should handle signals sending values unevenly", ^{
+ __block NSError *receivedError = nil;
+ __block BOOL hasCompleted = NO;
+
+ RACSubject *a = [RACSubject subject];
+ RACSubject *b = [RACSubject subject];
+ RACSubject *c = [RACSubject subject];
+
+ NSMutableArray *receivedValues = NSMutableArray.array;
+ NSArray *expectedValues = nil;
+
+ [[RACSignal zip:@[ a, b, c ] reduce:^(NSNumber *a, NSNumber *b, NSNumber *c) {
+ return [NSString stringWithFormat:@"%@%@%@", a, b, c];
+ }] subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ } error:^(NSError *error) {
+ receivedError = error;
+ } completed:^{
+ hasCompleted = YES;
+ }];
+
+ [a sendNext:@1];
+ [a sendNext:@2];
+ [a sendNext:@3];
+
+ [b sendNext:@1];
+
+ [c sendNext:@1];
+ [c sendNext:@2];
+
+ // a: [===......]
+ // b: [=........]
+ // c: [==.......]
+
+ expectedValues = @[ @"111" ];
+ expect(receivedValues).to.equal(expectedValues);
+ expect(receivedError).to.beNil();
+ expect(hasCompleted).to.beFalsy();
+
+ [b sendNext:@2];
+ [b sendNext:@3];
+ [b sendNext:@4];
+ [b sendCompleted];
+
+ // a: [===......]
+ // b: [====C....]
+ // c: [==.......]
+
+ expectedValues = @[ @"111", @"222" ];
+ expect(receivedValues).to.equal(expectedValues);
+ expect(receivedError).to.beNil();
+ expect(hasCompleted).to.beFalsy();
+
+ [c sendNext:@3];
+ [c sendNext:@4];
+ [c sendNext:@5];
+ [c sendError:RACSignalTestError];
+
+ // a: [===......]
+ // b: [====C....]
+ // c: [=====E...]
+
+ expectedValues = @[ @"111", @"222", @"333" ];
+ expect(receivedValues).to.equal(expectedValues);
+ expect(receivedError).to.equal(RACSignalTestError);
+ expect(hasCompleted).to.beFalsy();
+
+ [a sendNext:@4];
+ [a sendNext:@5];
+ [a sendNext:@6];
+ [a sendNext:@7];
+
+ // a: [=======..]
+ // b: [====C....]
+ // c: [=====E...]
+
+ expectedValues = @[ @"111", @"222", @"333" ];
+ expect(receivedValues).to.equal(expectedValues);
+ expect(receivedError).to.equal(RACSignalTestError);
+ expect(hasCompleted).to.beFalsy();
+ });
+
+ it(@"should handle multiples of the same side-effecting signal", ^{
+ __block NSUInteger counter = 0;
+ RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ ++counter;
+ [subscriber sendNext:@1];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+ RACSignal *combined = [RACSignal zip:@[ sideEffectingSignal, sideEffectingSignal ] reduce:^ NSString * (id x, id y) {
+ return [NSString stringWithFormat:@"%@%@", x, y];
+ }];
+ NSMutableArray *receivedValues = NSMutableArray.array;
+
+ expect(counter).to.equal(0);
+
+ [combined subscribeNext:^(id x) {
+ [receivedValues addObject:x];
+ }];
+
+ expect(counter).to.equal(2);
+ expect(receivedValues).to.equal(@[ @"11" ]);
+ });
+});
+
+describe(@"-sample:", ^{
+ it(@"should send the latest value when the sampler signal fires", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACSubject *sampleSubject = [RACSubject subject];
+ RACSignal *sampled = [subject sample:sampleSubject];
+ NSMutableArray *values = [NSMutableArray array];
+ [sampled subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ [sampleSubject sendNext:RACUnit.defaultUnit];
+ expect(values).to.equal(@[]);
+
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+ expect(values).to.equal(@[]);
+
+ [sampleSubject sendNext:RACUnit.defaultUnit];
+ NSArray *expected = @[ @2 ];
+ expect(values).to.equal(expected);
+
+ [subject sendNext:@3];
+ expect(values).to.equal(expected);
+
+ [sampleSubject sendNext:RACUnit.defaultUnit];
+ expected = @[ @2, @3 ];
+ expect(values).to.equal(expected);
+
+ [sampleSubject sendNext:RACUnit.defaultUnit];
+ expected = @[ @2, @3, @3 ];
+ expect(values).to.equal(expected);
+ });
+});
+
+describe(@"-collect", ^{
+ __block RACSubject *subject;
+ __block RACSignal *collected;
+
+ __block id value;
+ __block BOOL hasCompleted;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+ collected = [subject collect];
+
+ value = nil;
+ hasCompleted = NO;
+
+ [collected subscribeNext:^(id x) {
+ value = x;
+ } completed:^{
+ hasCompleted = YES;
+ }];
+ });
+
+ it(@"should send a single array when the original signal completes", ^{
+ NSArray *expected = @[ @1, @2, @3 ];
+
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+ [subject sendNext:@3];
+ expect(value).to.beNil();
+
+ [subject sendCompleted];
+ expect(value).to.equal(expected);
+ expect(hasCompleted).to.beTruthy();
+ });
+
+ it(@"should add NSNull to an array for nil values", ^{
+ NSArray *expected = @[ NSNull.null, @1, NSNull.null ];
+
+ [subject sendNext:nil];
+ [subject sendNext:@1];
+ [subject sendNext:nil];
+ expect(value).to.beNil();
+
+ [subject sendCompleted];
+ expect(value).to.equal(expected);
+ expect(hasCompleted).to.beTruthy();
+ });
+});
+
+describe(@"-bufferWithTime:", ^{
+ __block RACTestScheduler *scheduler;
+
+ __block RACSubject *input;
+ __block RACSignal *bufferedInput;
+ __block RACTuple *latestValue;
+
+ beforeEach(^{
+ scheduler = [[RACTestScheduler alloc] init];
+
+ input = [RACSubject subject];
+ bufferedInput = [input bufferWithTime:1 onScheduler:scheduler];
+ latestValue = nil;
+
+ [bufferedInput subscribeNext:^(RACTuple *x) {
+ latestValue = x;
+ }];
+ });
+
+ it(@"should buffer nexts", ^{
+ [input sendNext:@1];
+ [input sendNext:@2];
+
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@1, @2));
+
+ [input sendNext:@3];
+ [input sendNext:@4];
+
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@3, @4));
+ });
+
+ it(@"should not perform buffering until a value is sent", ^{
+ [input sendNext:@1];
+ [input sendNext:@2];
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@1, @2));
+
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@1, @2));
+
+ [input sendNext:@3];
+ [input sendNext:@4];
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@3, @4));
+ });
+
+ it(@"should flush any buffered nexts upon completion", ^{
+ [input sendNext:@1];
+ [input sendCompleted];
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(@1));
+ });
+
+ it(@"should support NSNull values", ^{
+ [input sendNext:NSNull.null];
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(NSNull.null));
+ });
+
+ it(@"should buffer nil values", ^{
+ [input sendNext:nil];
+ [scheduler stepAll];
+ expect(latestValue).to.equal(RACTuplePack(nil));
+ });
+});
+
+describe(@"-concat", ^{
+ __block RACSubject *subject;
+
+ __block RACSignal *oneSignal;
+ __block RACSignal *twoSignal;
+ __block RACSignal *threeSignal;
+
+ __block RACSignal *errorSignal;
+ __block RACSignal *completedSignal;
+
+ beforeEach(^{
+ subject = [RACReplaySubject subject];
+
+ oneSignal = [RACSignal return:@1];
+ twoSignal = [RACSignal return:@2];
+ threeSignal = [RACSignal return:@3];
+
+ errorSignal = [RACSignal error:RACSignalTestError];
+ completedSignal = RACSignal.empty;
+ });
+
+ it(@"should concatenate the values of inner signals", ^{
+ [subject sendNext:oneSignal];
+ [subject sendNext:twoSignal];
+ [subject sendNext:completedSignal];
+ [subject sendNext:threeSignal];
+
+ NSMutableArray *values = [NSMutableArray array];
+ [[subject concat] subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ NSArray *expected = @[ @1, @2, @3 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should complete only after all signals complete", ^{
+ RACReplaySubject *valuesSubject = [RACReplaySubject subject];
+
+ [subject sendNext:valuesSubject];
+ [subject sendCompleted];
+
+ [valuesSubject sendNext:@1];
+ [valuesSubject sendNext:@2];
+ [valuesSubject sendCompleted];
+
+ NSArray *expected = @[ @1, @2 ];
+ expect([[subject concat] toArray]).to.equal(expected);
+ });
+
+ it(@"should pass through errors", ^{
+ [subject sendNext:errorSignal];
+
+ NSError *error = nil;
+ [[subject concat] firstOrDefault:nil success:NULL error:&error];
+ expect(error).to.equal(RACSignalTestError);
+ });
+
+ it(@"should concat signals sent later", ^{
+ [subject sendNext:oneSignal];
+
+ NSMutableArray *values = [NSMutableArray array];
+ [[subject concat] subscribeNext:^(id x) {
+ [values addObject:x];
+ }];
+
+ NSArray *expected = @[ @1 ];
+ expect(values).to.equal(expected);
+
+ [subject sendNext:[twoSignal delay:0]];
+
+ expected = @[ @1, @2 ];
+ expect(values).will.equal(expected);
+
+ [subject sendNext:threeSignal];
+
+ expected = @[ @1, @2, @3 ];
+ expect(values).to.equal(expected);
+ });
+
+ it(@"should dispose the current signal", ^{
+ __block BOOL disposed = NO;
+ RACSignal *innerSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ RACDisposable *concatDisposable = [[subject concat] subscribeCompleted:^{}];
+
+ [subject sendNext:innerSignal];
+ expect(disposed).notTo.beTruthy();
+
+ [concatDisposable dispose];
+ expect(disposed).to.beTruthy();
+ });
+
+ it(@"should dispose later signals", ^{
+ __block BOOL disposed = NO;
+ RACSignal *laterSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ RACSubject *firstSignal = [RACSubject subject];
+ RACSignal *outerSignal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:firstSignal];
+ [subscriber sendNext:laterSignal];
+ return nil;
+ }];
+
+ RACDisposable *concatDisposable = [[outerSignal concat] subscribeCompleted:^{}];
+
+ [firstSignal sendCompleted];
+ expect(disposed).notTo.beTruthy();
+
+ [concatDisposable dispose];
+ expect(disposed).to.beTruthy();
+ });
+});
+
+describe(@"-initially:", ^{
+ __block RACSubject *subject;
+
+ __block NSUInteger initiallyInvokedCount;
+ __block RACSignal *signal;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+
+ initiallyInvokedCount = 0;
+ signal = [subject initially:^{
+ ++initiallyInvokedCount;
+ }];
+ });
+
+ it(@"should not run without a subscription", ^{
+ [subject sendCompleted];
+ expect(initiallyInvokedCount).to.equal(0);
+ });
+
+ it(@"should run on subscription", ^{
+ [signal subscribe:[RACSubscriber new]];
+ expect(initiallyInvokedCount).to.equal(1);
+ });
+
+ it(@"should re-run for each subscription", ^{
+ [signal subscribe:[RACSubscriber new]];
+ [signal subscribe:[RACSubscriber new]];
+ expect(initiallyInvokedCount).to.equal(2);
+ });
+});
+
+describe(@"-finally:", ^{
+ __block RACSubject *subject;
+
+ __block BOOL finallyInvoked;
+ __block RACSignal *signal;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+
+ finallyInvoked = NO;
+ signal = [subject finally:^{
+ finallyInvoked = YES;
+ }];
+ });
+
+ it(@"should not run finally without a subscription", ^{
+ [subject sendCompleted];
+ expect(finallyInvoked).to.beFalsy();
+ });
+
+ describe(@"with a subscription", ^{
+ __block RACDisposable *disposable;
+
+ beforeEach(^{
+ disposable = [signal subscribeCompleted:^{}];
+ });
+
+ afterEach(^{
+ [disposable dispose];
+ });
+
+ it(@"should not run finally upon next", ^{
+ [subject sendNext:RACUnit.defaultUnit];
+ expect(finallyInvoked).to.beFalsy();
+ });
+
+ it(@"should run finally upon completed", ^{
+ [subject sendCompleted];
+ expect(finallyInvoked).to.beTruthy();
+ });
+
+ it(@"should run finally upon error", ^{
+ [subject sendError:nil];
+ expect(finallyInvoked).to.beTruthy();
+ });
+ });
+});
+
+describe(@"-ignoreValues", ^{
+ __block RACSubject *subject;
+
+ __block BOOL gotNext;
+ __block BOOL gotCompleted;
+ __block NSError *receivedError;
+
+ beforeEach(^{
+ subject = [RACSubject subject];
+
+ gotNext = NO;
+ gotCompleted = NO;
+ receivedError = nil;
+
+ [[subject ignoreValues] subscribeNext:^(id _) {
+ gotNext = YES;
+ } error:^(NSError *error) {
+ receivedError = error;
+ } completed:^{
+ gotCompleted = YES;
+ }];
+ });
+
+ it(@"should skip nexts and pass through completed", ^{
+ [subject sendNext:RACUnit.defaultUnit];
+ [subject sendCompleted];
+
+ expect(gotNext).to.beFalsy();
+ expect(gotCompleted).to.beTruthy();
+ expect(receivedError).to.beNil();
+ });
+
+ it(@"should skip nexts and pass through errors", ^{
+ [subject sendNext:RACUnit.defaultUnit];
+ [subject sendError:RACSignalTestError];
+
+ expect(gotNext).to.beFalsy();
+ expect(gotCompleted).to.beFalsy();
+ expect(receivedError).to.equal(RACSignalTestError);
+ });
+});
+
+describe(@"-materialize", ^{
+ it(@"should convert nexts and completed into RACEvents", ^{
+ NSArray *events = [[[RACSignal return:RACUnit.defaultUnit] materialize] toArray];
+ NSArray *expected = @[
+ [RACEvent eventWithValue:RACUnit.defaultUnit],
+ RACEvent.completedEvent
+ ];
+
+ expect(events).to.equal(expected);
+ });
+
+ it(@"should convert errors into RACEvents and complete", ^{
+ NSArray *events = [[[RACSignal error:RACSignalTestError] materialize] toArray];
+ NSArray *expected = @[ [RACEvent eventWithError:RACSignalTestError] ];
+ expect(events).to.equal(expected);
+ });
+});
+
+describe(@"-dematerialize", ^{
+ it(@"should convert nexts from RACEvents", ^{
+ RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:[RACEvent eventWithValue:@1]];
+ [subscriber sendNext:[RACEvent eventWithValue:@2]];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ NSArray *expected = @[ @1, @2 ];
+ expect([[events dematerialize] toArray]).to.equal(expected);
+ });
+
+ it(@"should convert completed from a RACEvent", ^{
+ RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:[RACEvent eventWithValue:@1]];
+ [subscriber sendNext:RACEvent.completedEvent];
+ [subscriber sendNext:[RACEvent eventWithValue:@2]];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ NSArray *expected = @[ @1 ];
+ expect([[events dematerialize] toArray]).to.equal(expected);
+ });
+
+ it(@"should convert error from a RACEvent", ^{
+ RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:[RACEvent eventWithError:RACSignalTestError]];
+ [subscriber sendNext:[RACEvent eventWithValue:@1]];
+ [subscriber sendCompleted];
+ return nil;
+ }];
+
+ __block NSError *error = nil;
+ expect([[events dematerialize] firstOrDefault:nil success:NULL error:&error]).to.beNil();
+ expect(error).to.equal(RACSignalTestError);
+ });
+});
+
+describe(@"-not", ^{
+ it(@"should invert every BOOL sent", ^{
+ RACSubject *subject = [RACReplaySubject subject];
+ [subject sendNext:@NO];
+ [subject sendNext:@YES];
+ [subject sendCompleted];
+ NSArray *results = [[subject not] toArray];
+ NSArray *expected = @[ @YES, @NO ];
+ expect(results).to.equal(expected);
+ });
+});
+
+describe(@"-and", ^{
+ it(@"should return YES if all YES values are sent", ^{
+ RACSubject *subject = [RACReplaySubject subject];
+
+ [subject sendNext:RACTuplePack(@YES, @NO, @YES)];
+ [subject sendNext:RACTuplePack(@NO, @NO, @NO)];
+ [subject sendNext:RACTuplePack(@YES, @YES, @YES)];
+ [subject sendCompleted];
+
+ NSArray *results = [[subject and] toArray];
+ NSArray *expected = @[ @NO, @NO, @YES ];
+
+ expect(results).to.equal(expected);
+ });
+});
+
+describe(@"-or", ^{
+ it(@"should return YES for any YES values sent", ^{
+ RACSubject *subject = [RACReplaySubject subject];
+
+ [subject sendNext:RACTuplePack(@YES, @NO, @YES)];
+ [subject sendNext:RACTuplePack(@NO, @NO, @NO)];
+ [subject sendCompleted];
+
+ NSArray *results = [[subject or] toArray];
+ NSArray *expected = @[ @YES, @NO ];
+
+ expect(results).to.equal(expected);
+ });
+});
+
+describe(@"-groupBy:", ^{
+ it(@"should send completed to all grouped signals.", ^{
+ RACSubject *subject = [RACReplaySubject subject];
+
+ __block NSUInteger groupedSignalCount = 0;
+ __block NSUInteger completedGroupedSignalCount = 0;
+ [[subject groupBy:^(NSNumber *number) {
+ return @(floorf(number.floatValue));
+ }] subscribeNext:^(RACGroupedSignal *groupedSignal) {
+ ++groupedSignalCount;
+
+ [groupedSignal subscribeCompleted:^{
+ ++completedGroupedSignalCount;
+ }];
+ }];
+
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+ [subject sendCompleted];
+
+ expect(completedGroupedSignalCount).to.equal(groupedSignalCount);
+ });
+
+ it(@"should send error to all grouped signals.", ^{
+ RACSubject *subject = [RACReplaySubject subject];
+
+ __block NSUInteger groupedSignalCount = 0;
+ __block NSUInteger erroneousGroupedSignalCount = 0;
+ [[subject groupBy:^(NSNumber *number) {
+ return @(floorf(number.floatValue));
+ }] subscribeNext:^(RACGroupedSignal *groupedSignal) {
+ ++groupedSignalCount;
+
+ [groupedSignal subscribeError:^(NSError *error) {
+ ++erroneousGroupedSignalCount;
+
+ expect(error.domain).to.equal(@"TestDomain");
+ expect(error.code).to.equal(123);
+ }];
+ }];
+
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+ [subject sendError:[NSError errorWithDomain:@"TestDomain" code:123 userInfo:nil]];
+
+ expect(erroneousGroupedSignalCount).to.equal(groupedSignalCount);
+ });
+});
+
+describe(@"starting signals", ^{
+ describe(@"+startLazilyWithScheduler:block:", ^{
+ itBehavesLike(RACSignalStartSharedExamplesName, ^{
+ NSArray *expectedValues = @[ @42, @43 ];
+ RACScheduler *scheduler = [RACScheduler scheduler];
+ RACSignal *signal = [RACSignal startLazilyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
+ for (id value in expectedValues) {
+ [subscriber sendNext:value];
+ }
+ [subscriber sendCompleted];
+ }];
+ return @{
+ RACSignalStartSignal: signal,
+ RACSignalStartExpectedValues: expectedValues,
+ RACSignalStartExpectedScheduler: scheduler,
+ };
+ });
+
+ __block NSUInteger invokedCount = 0;
+ __block void (^subscribe)(void);
+
+ beforeEach(^{
+ invokedCount = 0;
+
+ RACSignal *signal = [RACSignal startLazilyWithScheduler:RACScheduler.immediateScheduler block:^(id<RACSubscriber> subscriber) {
+ invokedCount++;
+ [subscriber sendNext:@42];
+ [subscriber sendCompleted];
+ }];
+
+ subscribe = [^{
+ [signal subscribe:[RACSubscriber subscriberWithNext:nil error:nil completed:nil]];
+ } copy];
+ });
+
+ it(@"should only invoke the block on subscription", ^{
+ expect(invokedCount).to.equal(0);
+ subscribe();
+ expect(invokedCount).to.equal(1);
+ });
+
+ it(@"should only invoke the block once", ^{
+ expect(invokedCount).to.equal(0);
+ subscribe();
+ expect(invokedCount).to.equal(1);
+ subscribe();
+ expect(invokedCount).to.equal(1);
+ subscribe();
+ expect(invokedCount).to.equal(1);
+ });
+
+ it(@"should invoke the block on the given scheduler", ^{
+ RACScheduler *scheduler = [RACScheduler scheduler];
+ __block RACScheduler *currentScheduler;
+ [[[RACSignal
+ startLazilyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
+ currentScheduler = RACScheduler.currentScheduler;
+ }]
+ publish]
+ connect];
+
+ expect(currentScheduler).will.equal(scheduler);
+ });
+ });
+
+ describe(@"+startEagerlyWithScheduler:block:", ^{
+ itBehavesLike(RACSignalStartSharedExamplesName, ^{
+ NSArray *expectedValues = @[ @42, @43 ];
+ RACScheduler *scheduler = [RACScheduler scheduler];
+ RACSignal *signal = [RACSignal startEagerlyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
+ for (id value in expectedValues) {
+ [subscriber sendNext:value];
+ }
+ [subscriber sendCompleted];
+ }];
+ return @{
+ RACSignalStartSignal: signal,
+ RACSignalStartExpectedValues: expectedValues,
+ RACSignalStartExpectedScheduler: scheduler,
+ };
+ });
+
+ it(@"should immediately invoke the block", ^{
+ __block BOOL blockInvoked = NO;
+ [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
+ blockInvoked = YES;
+ }];
+
+ expect(blockInvoked).will.beTruthy();
+ });
+
+ it(@"should only invoke the block once", ^{
+ __block NSUInteger invokedCount = 0;
+ RACSignal *signal = [RACSignal startEagerlyWithScheduler:RACScheduler.immediateScheduler block:^(id<RACSubscriber> subscriber) {
+ invokedCount++;
+ }];
+
+ expect(invokedCount).to.equal(1);
+
+ [[signal publish] connect];
+ expect(invokedCount).to.equal(1);
+
+ [[signal publish] connect];
+ expect(invokedCount).to.equal(1);
+ });
+
+ it(@"should invoke the block on the given scheduler", ^{
+ RACScheduler *scheduler = [RACScheduler scheduler];
+ __block RACScheduler *currentScheduler;
+ [RACSignal startEagerlyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+
+ expect(currentScheduler).will.equal(scheduler);
+ });
+ });
+});
+
+describe(@"-toArray", ^{
+ __block RACSubject *subject;
+
+ beforeEach(^{
+ subject = [RACReplaySubject subject];
+ });
+
+ it(@"should return an array which contains NSNulls for nil values", ^{
+ NSArray *expected = @[ NSNull.null, @1, NSNull.null ];
+
+ [subject sendNext:nil];
+ [subject sendNext:@1];
+ [subject sendNext:nil];
+ [subject sendCompleted];
+
+ expect([subject toArray]).to.equal(expected);
+ });
+
+ it(@"should return nil upon error", ^{
+ [subject sendError:nil];
+ expect([subject toArray]).to.beNil();
+ });
+
+ it(@"should return nil upon error even if some nexts were sent", ^{
+ [subject sendNext:@1];
+ [subject sendNext:@2];
+ [subject sendError:nil];
+
+ expect([subject toArray]).to.beNil();
+ });
+});
+
+describe(@"-ignore:", ^{
+ it(@"should ignore nil", ^{
+ RACSignal *signal = [[RACSignal
+ createSignal:^ id (id<RACSubscriber> subscriber) {
+ [subscriber sendNext:@1];
+ [subscriber sendNext:nil];
+ [subscriber sendNext:@3];
+ [subscriber sendNext:@4];
+ [subscriber sendNext:nil];
+ [subscriber sendCompleted];
+ return nil;
+ }]
+ ignore:nil];
+
+ NSArray *expected = @[ @1, @3, @4 ];
+ expect([signal toArray]).to.equal(expected);
+ });
+});
+
+describe(@"-replayLazily", ^{
+ __block NSUInteger subscriptionCount;
+ __block BOOL disposed;
+
+ __block RACSignal *signal;
+ __block RACSubject *disposeSubject;
+ __block RACSignal *replayedSignal;
+
+ beforeEach(^{
+ subscriptionCount = 0;
+ disposed = NO;
+
+ signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
+ subscriptionCount++;
+ [subscriber sendNext:RACUnit.defaultUnit];
+
+ RACDisposable *schedulingDisposable = [RACScheduler.mainThreadScheduler schedule:^{
+ [subscriber sendNext:RACUnit.defaultUnit];
+ [subscriber sendCompleted];
+ }];
+
+ return [RACDisposable disposableWithBlock:^{
+ [schedulingDisposable dispose];
+ disposed = YES;
+ }];
+ }];
+
+ disposeSubject = [RACSubject subject];
+ replayedSignal = [[signal takeUntil:disposeSubject] replayLazily];
+ });
+
+ it(@"should forward the input signal upon subscription", ^{
+ expect(subscriptionCount).to.equal(0);
+
+ expect([replayedSignal asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
+ expect(subscriptionCount).to.equal(1);
+ });
+
+ it(@"should replay the input signal for future subscriptions", ^{
+ NSArray *events = [[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL];
+ expect(events).notTo.beNil();
+
+ expect([[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(events);
+ expect(subscriptionCount).to.equal(1);
+ });
+
+ it(@"should replay even after disposal", ^{
+ __block NSUInteger valueCount = 0;
+ [replayedSignal subscribeNext:^(id x) {
+ valueCount++;
+ }];
+
+ [disposeSubject sendCompleted];
+ expect(valueCount).to.equal(1);
+ expect([[replayedSignal toArray] count]).to.equal(valueCount);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.h
new file mode 100644
index 0000000..3e01c36
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.h
@@ -0,0 +1,20 @@
+//
+// RACSignalStartExamples.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/29/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+extern NSString * const RACSignalStartSharedExamplesName;
+
+// The signal to test, created by some +start...: variation.
+extern NSString * const RACSignalStartSignal;
+
+// An NSArray of the values which the signal should be expected to send.
+extern NSString * const RACSignalStartExpectedValues;
+
+// The scheduler on which the signal should be expected to send values.
+extern NSString * const RACSignalStartExpectedScheduler;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.m
new file mode 100644
index 0000000..6416630
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSignalStartExamples.m
@@ -0,0 +1,74 @@
+//
+// RACSignalStartExamples.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 5/29/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignalStartExamples.h"
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "RACScheduler.h"
+#import "RACSubscriber.h"
+#import "RACMulticastConnection.h"
+
+NSString * const RACSignalStartSharedExamplesName = @"RACSignalStartSharedExamplesName";
+
+NSString * const RACSignalStartSignal = @"RACSignalStartSignal";
+NSString * const RACSignalStartExpectedValues = @"RACSignalStartExpectedValues";
+NSString * const RACSignalStartExpectedScheduler = @"RACSignalStartExpectedScheduler";
+
+SharedExampleGroupsBegin(RACSignalStartSpec)
+
+sharedExamples(RACSignalStartSharedExamplesName, ^(NSDictionary *data) {
+ __block RACSignal *signal;
+ __block NSArray *expectedValues;
+ __block RACScheduler *scheduler;
+ __block RACScheduler * (^subscribeAndGetScheduler)(void);
+
+ beforeEach(^{
+ signal = data[RACSignalStartSignal];
+ expectedValues = data[RACSignalStartExpectedValues];
+ scheduler = data[RACSignalStartExpectedScheduler];
+
+ subscribeAndGetScheduler = [^{
+ __block RACScheduler *schedulerInDelivery;
+ [signal subscribeNext:^(id _) {
+ schedulerInDelivery = RACScheduler.currentScheduler;
+ }];
+
+ expect(schedulerInDelivery).willNot.beNil();
+ return schedulerInDelivery;
+ } copy];
+ });
+
+ it(@"should send values from the returned signal", ^{
+ NSArray *values = [signal toArray];
+ expect(values).to.equal(expectedValues);
+ });
+
+ it(@"should replay all values", ^{
+ // Force a subscription so that we get replayed results.
+ [[signal publish] connect];
+
+ NSArray *values = [signal toArray];
+ expect(values).to.equal(expectedValues);
+ });
+
+ it(@"should deliver the original results on the given scheduler", ^{
+ RACScheduler *currentScheduler = subscribeAndGetScheduler();
+ expect(currentScheduler).to.equal(scheduler);
+ });
+
+ it(@"should deliver replayed results on the given scheduler", ^{
+ // Force a subscription so that we get replayed results on the
+ // tested subscription.
+ subscribeAndGetScheduler();
+
+ RACScheduler *currentScheduler = subscribeAndGetScheduler();
+ expect(currentScheduler).to.equal(scheduler);
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.h
new file mode 100644
index 0000000..318a6a8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.h
@@ -0,0 +1,26 @@
+//
+// RACStreamExamples.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for a RACStream subclass.
+extern NSString * const RACStreamExamples;
+
+// The RACStream subclass to test.
+extern NSString * const RACStreamExamplesClass;
+
+// An infinite RACStream to test, making sure that certain operations
+// terminate.
+//
+// The stream should contain infinite RACUnit values.
+extern NSString * const RACStreamExamplesInfiniteStream;
+
+// A block with the signature:
+//
+// void (^)(RACStream *stream, NSArray *expectedValues)
+//
+// … used to verify that a stream contains the expected values.
+extern NSString * const RACStreamExamplesVerifyValuesBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.m
new file mode 100644
index 0000000..bdf22f1
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.m
@@ -0,0 +1,648 @@
+//
+// RACStreamExamples.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-01.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACStreamExamples.h"
+
+#import "RACStream.h"
+#import "RACUnit.h"
+#import "RACTuple.h"
+
+NSString * const RACStreamExamples = @"RACStreamExamples";
+NSString * const RACStreamExamplesClass = @"RACStreamExamplesClass";
+NSString * const RACStreamExamplesInfiniteStream = @"RACStreamExamplesInfiniteStream";
+NSString * const RACStreamExamplesVerifyValuesBlock = @"RACStreamExamplesVerifyValuesBlock";
+
+SharedExampleGroupsBegin(RACStreamExamples)
+
+sharedExamplesFor(RACStreamExamples, ^(NSDictionary *data) {
+ __block Class streamClass;
+ __block void (^verifyValues)(RACStream *, NSArray *);
+ __block RACStream *infiniteStream;
+
+ __block RACStream *(^streamWithValues)(NSArray *);
+
+ before(^{
+ streamClass = data[RACStreamExamplesClass];
+ verifyValues = data[RACStreamExamplesVerifyValuesBlock];
+ infiniteStream = data[RACStreamExamplesInfiniteStream];
+ streamWithValues = [^(NSArray *values) {
+ RACStream *stream = [streamClass empty];
+
+ for (id value in values) {
+ stream = [stream concat:[streamClass return:value]];
+ }
+
+ return stream;
+ } copy];
+ });
+
+ it(@"should return an empty stream", ^{
+ RACStream *stream = [streamClass empty];
+ verifyValues(stream, @[]);
+ });
+
+ it(@"should lift a value into a stream", ^{
+ RACStream *stream = [streamClass return:RACUnit.defaultUnit];
+ verifyValues(stream, @[ RACUnit.defaultUnit ]);
+ });
+
+ describe(@"-concat:", ^{
+ it(@"should concatenate two streams", ^{
+ RACStream *stream = [[streamClass return:@0] concat:[streamClass return:@1]];
+ verifyValues(stream, @[ @0, @1 ]);
+ });
+
+ it(@"should concatenate three streams", ^{
+ RACStream *stream = [[[streamClass return:@0] concat:[streamClass return:@1]] concat:[streamClass return:@2]];
+ verifyValues(stream, @[ @0, @1, @2 ]);
+ });
+
+ it(@"should concatenate around an empty stream", ^{
+ RACStream *stream = [[[streamClass return:@0] concat:[streamClass empty]] concat:[streamClass return:@2]];
+ verifyValues(stream, @[ @0, @2 ]);
+ });
+ });
+
+ it(@"should flatten", ^{
+ RACStream *stream = [[streamClass return:[streamClass return:RACUnit.defaultUnit]] flatten];
+ verifyValues(stream, @[ RACUnit.defaultUnit ]);
+ });
+
+ describe(@"-bind:", ^{
+ it(@"should return the result of binding a single value", ^{
+ RACStream *stream = [[streamClass return:@0] bind:^{
+ return ^(NSNumber *value, BOOL *stop) {
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ };
+ }];
+
+ verifyValues(stream, @[ @1 ]);
+ });
+
+ it(@"should concatenate the result of binding multiple values", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1 ]);
+ RACStream *stream = [baseStream bind:^{
+ return ^(NSNumber *value, BOOL *stop) {
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ };
+ }];
+
+ verifyValues(stream, @[ @1, @2 ]);
+ });
+
+ it(@"should concatenate with an empty result from binding a value", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
+ RACStream *stream = [baseStream bind:^{
+ return ^(NSNumber *value, BOOL *stop) {
+ if (value.integerValue == 1) return [streamClass empty];
+
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ };
+ }];
+
+ verifyValues(stream, @[ @1, @3 ]);
+ });
+
+ it(@"should terminate immediately when returning nil", ^{
+ RACStream *stream = [infiniteStream bind:^{
+ return ^ id (id _, BOOL *stop) {
+ return nil;
+ };
+ }];
+
+ verifyValues(stream, @[]);
+ });
+
+ it(@"should terminate after one value when setting 'stop'", ^{
+ RACStream *stream = [infiniteStream bind:^{
+ return ^ id (id value, BOOL *stop) {
+ *stop = YES;
+ return [streamClass return:value];
+ };
+ }];
+
+ verifyValues(stream, @[ RACUnit.defaultUnit ]);
+ });
+
+ it(@"should terminate immediately when returning nil and setting 'stop'", ^{
+ RACStream *stream = [infiniteStream bind:^{
+ return ^ id (id _, BOOL *stop) {
+ *stop = YES;
+ return nil;
+ };
+ }];
+
+ verifyValues(stream, @[]);
+ });
+
+ it(@"should be restartable even with block state", ^{
+ NSArray *values = @[ @0, @1, @2 ];
+ RACStream *baseStream = streamWithValues(values);
+
+ RACStream *countingStream = [baseStream bind:^{
+ __block NSUInteger counter = 0;
+
+ return ^(id x, BOOL *stop) {
+ return [streamClass return:@(counter++)];
+ };
+ }];
+
+ verifyValues(countingStream, @[ @0, @1, @2 ]);
+ verifyValues(countingStream, @[ @0, @1, @2 ]);
+ });
+
+ it(@"should be interleavable even with block state", ^{
+ NSArray *values = @[ @0, @1, @2 ];
+ RACStream *baseStream = streamWithValues(values);
+
+ RACStream *countingStream = [baseStream bind:^{
+ __block NSUInteger counter = 0;
+
+ return ^(id x, BOOL *stop) {
+ return [streamClass return:@(counter++)];
+ };
+ }];
+
+ // Just so +zip:reduce: thinks this is a unique stream.
+ RACStream *anotherStream = [[streamClass empty] concat:countingStream];
+
+ RACStream *zipped = [streamClass zip:@[ countingStream, anotherStream ] reduce:^(NSNumber *v1, NSNumber *v2) {
+ return @(v1.integerValue + v2.integerValue);
+ }];
+
+ verifyValues(zipped, @[ @0, @2, @4 ]);
+ });
+ });
+
+ describe(@"-flattenMap:", ^{
+ it(@"should return a single mapped result", ^{
+ RACStream *stream = [[streamClass return:@0] flattenMap:^(NSNumber *value) {
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ }];
+
+ verifyValues(stream, @[ @1 ]);
+ });
+
+ it(@"should concatenate the results of mapping multiple values", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1 ]);
+ RACStream *stream = [baseStream flattenMap:^(NSNumber *value) {
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ }];
+
+ verifyValues(stream, @[ @1, @2 ]);
+ });
+
+ it(@"should concatenate with an empty result from mapping a value", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
+ RACStream *stream = [baseStream flattenMap:^(NSNumber *value) {
+ if (value.integerValue == 1) return [streamClass empty];
+
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ }];
+
+ verifyValues(stream, @[ @1, @3 ]);
+ });
+
+ it(@"should treat nil streams like empty streams", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
+ RACStream *stream = [baseStream flattenMap:^ RACStream * (NSNumber *value) {
+ if (value.integerValue == 1) return nil;
+
+ NSNumber *newValue = @(value.integerValue + 1);
+ return [streamClass return:newValue];
+ }];
+
+ verifyValues(stream, @[ @1, @3 ]);
+ });
+ });
+
+ it(@"should map", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
+ RACStream *stream = [baseStream map:^(NSNumber *value) {
+ return @(value.integerValue + 1);
+ }];
+
+ verifyValues(stream, @[ @1, @2, @3 ]);
+ });
+
+ it(@"should map and replace", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]);
+ RACStream *stream = [baseStream mapReplace:RACUnit.defaultUnit];
+
+ verifyValues(stream, @[ RACUnit.defaultUnit, RACUnit.defaultUnit, RACUnit.defaultUnit ]);
+ });
+
+ it(@"should filter", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]);
+ RACStream *stream = [baseStream filter:^ BOOL (NSNumber *value) {
+ return value.integerValue % 2 == 0;
+ }];
+
+ verifyValues(stream, @[ @0, @2, @4, @6 ]);
+ });
+
+ describe(@"-ignore:", ^{
+ it(@"should ignore a value", ^{
+ RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]);
+ RACStream *stream = [baseStream ignore:@1];
+
+ verifyValues(stream, @[ @0, @2, @3, @4, @5, @6 ]);
+ });
+
+ it(@"should ignore based on object equality", ^{
+ RACStream *baseStream = streamWithValues(@[ @"0", @"1", @"2", @"3", @"4", @"5", @"6" ]);
+
+ NSMutableString *valueToIgnore = [[NSMutableString alloc] init];
+ [valueToIgnore appendString:@"1"];
+ RACStream *stream = [baseStream ignore:valueToIgnore];
+
+ verifyValues(stream, @[ @"0", @"2", @"3", @"4", @"5", @"6" ]);
+ });
+ });
+
+ it(@"should start with a value", ^{
+ RACStream *stream = [[streamClass return:@1] startWith:@0];
+ verifyValues(stream, @[ @0, @1 ]);
+ });
+
+ describe(@"-skip:", ^{
+ __block NSArray *values;
+ __block RACStream *stream;
+
+ before(^{
+ values = @[ @0, @1, @2 ];
+ stream = streamWithValues(values);
+ });
+
+ it(@"should skip any valid number of values", ^{
+ for (NSUInteger i = 0; i < values.count; i++) {
+ verifyValues([stream skip:i], [values subarrayWithRange:NSMakeRange(i, values.count - i)]);
+ }
+ });
+
+ it(@"should return an empty stream when skipping too many values", ^{
+ verifyValues([stream skip:4], @[]);
+ });
+ });
+
+ describe(@"-take:", ^{
+ describe(@"with three values", ^{
+ __block NSArray *values;
+ __block RACStream *stream;
+
+ before(^{
+ values = @[ @0, @1, @2 ];
+ stream = streamWithValues(values);
+ });
+
+ it(@"should take any valid number of values", ^{
+ for (NSUInteger i = 0; i < values.count; i++) {
+ verifyValues([stream take:i], [values subarrayWithRange:NSMakeRange(0, i)]);
+ }
+ });
+
+ it(@"should return the same stream when taking too many values", ^{
+ verifyValues([stream take:4], values);
+ });
+ });
+
+ it(@"should take and terminate from an infinite stream", ^{
+ verifyValues([infiniteStream take:0], @[]);
+ verifyValues([infiniteStream take:1], @[ RACUnit.defaultUnit ]);
+ verifyValues([infiniteStream take:2], @[ RACUnit.defaultUnit, RACUnit.defaultUnit ]);
+ });
+
+ it(@"should take and terminate from a single-item stream", ^{
+ NSArray *values = @[ RACUnit.defaultUnit ];
+ RACStream *stream = streamWithValues(values);
+ verifyValues([stream take:1], values);
+ });
+ });
+
+ describe(@"zip stream creation methods", ^{
+ __block NSArray *valuesOne;
+
+ __block RACStream *streamOne;
+ __block RACStream *streamTwo;
+ __block RACStream *streamThree;
+ __block NSArray *threeStreams;
+
+ __block NSArray *oneStreamTuples;
+ __block NSArray *twoStreamTuples;
+ __block NSArray *threeStreamTuples;
+
+ before(^{
+ valuesOne = @[ @"Ada", @"Bob", @"Dea" ];
+ NSArray *valuesTwo = @[ @"eats", @"cooks", @"jumps" ];
+ NSArray *valuesThree = @[ @"fish", @"bear", @"rock" ];
+
+ streamOne = streamWithValues(valuesOne);
+ streamTwo = streamWithValues(valuesTwo);
+ streamThree = streamWithValues(valuesThree);
+ threeStreams = @[ streamOne, streamTwo, streamThree ];
+
+ oneStreamTuples = @[
+ RACTuplePack(valuesOne[0]),
+ RACTuplePack(valuesOne[1]),
+ RACTuplePack(valuesOne[2]),
+ ];
+
+ twoStreamTuples = @[
+ RACTuplePack(valuesOne[0], valuesTwo[0]),
+ RACTuplePack(valuesOne[1], valuesTwo[1]),
+ RACTuplePack(valuesOne[2], valuesTwo[2]),
+ ];
+
+ threeStreamTuples = @[
+ RACTuplePack(valuesOne[0], valuesTwo[0], valuesThree[0]),
+ RACTuplePack(valuesOne[1], valuesTwo[1], valuesThree[1]),
+ RACTuplePack(valuesOne[2], valuesTwo[2], valuesThree[2]),
+ ];
+ });
+
+ describe(@"-zipWith:", ^{
+ it(@"should make a stream of tuples", ^{
+ RACStream *stream = [streamOne zipWith:streamTwo];
+ verifyValues(stream, twoStreamTuples);
+ });
+
+ it(@"should truncate streams", ^{
+ RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]);
+ RACStream *stream = [streamOne zipWith:shortStream];
+
+ verifyValues(stream, @[
+ RACTuplePack(valuesOne[0], @"now"),
+ RACTuplePack(valuesOne[1], @"later")
+ ]);
+ });
+
+ it(@"should work on infinite streams", ^{
+ RACStream *stream = [streamOne zipWith:infiniteStream];
+ verifyValues(stream, @[
+ RACTuplePack(valuesOne[0], RACUnit.defaultUnit),
+ RACTuplePack(valuesOne[1], RACUnit.defaultUnit),
+ RACTuplePack(valuesOne[2], RACUnit.defaultUnit)
+ ]);
+ });
+
+ it(@"should handle multiples of the same stream", ^{
+ RACStream *stream = [streamOne zipWith:streamOne];
+ verifyValues(stream, @[
+ RACTuplePack(valuesOne[0], valuesOne[0]),
+ RACTuplePack(valuesOne[1], valuesOne[1]),
+ RACTuplePack(valuesOne[2], valuesOne[2]),
+ ]);
+ });
+ });
+
+ describe(@"+zip:reduce:", ^{
+ it(@"should reduce values", ^{
+ RACStream *stream = [streamClass zip:threeStreams reduce:^ NSString * (id x, id y, id z) {
+ return [NSString stringWithFormat:@"%@ %@ %@", x, y, z];
+ }];
+ verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]);
+ });
+
+ it(@"should truncate streams", ^{
+ RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]);
+ NSArray *streams = [threeStreams arrayByAddingObject:shortStream];
+ RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) {
+ return [NSString stringWithFormat:@"%@ %@ %@ %@", w, x, y, z];
+ }];
+ verifyValues(stream, @[ @"Ada eats fish now", @"Bob cooks bear later" ]);
+ });
+
+ it(@"should work on infinite streams", ^{
+ NSArray *streams = [threeStreams arrayByAddingObject:infiniteStream];
+ RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) {
+ return [NSString stringWithFormat:@"%@ %@ %@", w, x, y];
+ }];
+ verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]);
+ });
+
+ it(@"should handle multiples of the same stream", ^{
+ NSArray *streams = @[ streamOne, streamOne, streamTwo, streamThree, streamTwo, streamThree ];
+ RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id x1, id x2, id y1, id z1, id y2, id z2) {
+ return [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@", x1, x2, y1, z1, y2, z2];
+ }];
+ verifyValues(stream, @[ @"Ada Ada eats fish eats fish", @"Bob Bob cooks bear cooks bear", @"Dea Dea jumps rock jumps rock" ]);
+ });
+ });
+
+ describe(@"+zip:", ^{
+ it(@"should make a stream of tuples out of single value", ^{
+ RACStream *stream = [streamClass zip:@[ streamOne ]];
+ verifyValues(stream, oneStreamTuples);
+ });
+
+ it(@"should make a stream of tuples out of an array of streams", ^{
+ RACStream *stream = [streamClass zip:threeStreams];
+ verifyValues(stream, threeStreamTuples);
+ });
+
+ it(@"should make an empty stream if given an empty array", ^{
+ RACStream *stream = [streamClass zip:@[]];
+ verifyValues(stream, @[]);
+ });
+
+ it(@"should make a stream of tuples out of an enumerator of streams", ^{
+ RACStream *stream = [streamClass zip:threeStreams.objectEnumerator];
+ verifyValues(stream, threeStreamTuples);
+ });
+
+ it(@"should make an empty stream if given an empty enumerator", ^{
+ RACStream *stream = [streamClass zip:@[].objectEnumerator];
+ verifyValues(stream, @[]);
+ });
+ });
+ });
+
+ describe(@"+concat:", ^{
+ __block NSArray *streams = nil;
+ __block NSArray *result = nil;
+
+ before(^{
+ RACStream *a = [streamClass return:@0];
+ RACStream *b = [streamClass empty];
+ RACStream *c = streamWithValues(@[ @1, @2, @3 ]);
+ RACStream *d = [streamClass return:@4];
+ RACStream *e = [streamClass return:@5];
+ RACStream *f = [streamClass empty];
+ RACStream *g = [streamClass empty];
+ RACStream *h = streamWithValues(@[ @6, @7 ]);
+ streams = @[ a, b, c, d, e, f, g, h ];
+ result = @[ @0, @1, @2, @3, @4, @5, @6, @7 ];
+ });
+
+ it(@"should concatenate an array of streams", ^{
+ RACStream *stream = [streamClass concat:streams];
+ verifyValues(stream, result);
+ });
+
+ it(@"should concatenate an enumerator of streams", ^{
+ RACStream *stream = [streamClass concat:streams.objectEnumerator];
+ verifyValues(stream, result);
+ });
+ });
+
+ it(@"should scan", ^{
+ RACStream *stream = streamWithValues(@[ @1, @2, @3, @4 ]);
+ RACStream *scanned = [stream scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) {
+ return @(running.integerValue + next.integerValue);
+ }];
+
+ verifyValues(scanned, @[ @1, @3, @6, @10 ]);
+ });
+
+ describe(@"taking with a predicate", ^{
+ NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ];
+
+ __block RACStream *stream;
+
+ before(^{
+ stream = streamWithValues(values);
+ });
+
+ it(@"should take until a predicate is true", ^{
+ RACStream *taken = [stream takeUntilBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue >= 3;
+ }];
+
+ verifyValues(taken, @[ @0, @1, @2 ]);
+ });
+
+ it(@"should take while a predicate is true", ^{
+ RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue <= 1;
+ }];
+
+ verifyValues(taken, @[ @0, @1 ]);
+ });
+
+ it(@"should take a full stream", ^{
+ RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue <= 10;
+ }];
+
+ verifyValues(taken, values);
+ });
+
+ it(@"should return an empty stream", ^{
+ RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue < 0;
+ }];
+
+ verifyValues(taken, @[]);
+ });
+
+ it(@"should terminate an infinite stream", ^{
+ RACStream *infiniteCounter = [infiniteStream scanWithStart:@0 reduce:^(NSNumber *running, id _) {
+ return @(running.unsignedIntegerValue + 1);
+ }];
+
+ RACStream *taken = [infiniteCounter takeWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue <= 5;
+ }];
+
+ verifyValues(taken, @[ @1, @2, @3, @4, @5 ]);
+ });
+ });
+
+ describe(@"skipping with a predicate", ^{
+ NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ];
+
+ __block RACStream *stream;
+
+ before(^{
+ stream = streamWithValues(values);
+ });
+
+ it(@"should skip until a predicate is true", ^{
+ RACStream *taken = [stream skipUntilBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue >= 3;
+ }];
+
+ verifyValues(taken, @[ @3, @0, @2, @4 ]);
+ });
+
+ it(@"should skip while a predicate is true", ^{
+ RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue <= 1;
+ }];
+
+ verifyValues(taken, @[ @2, @3, @0, @2, @4 ]);
+ });
+
+ it(@"should skip a full stream", ^{
+ RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue <= 10;
+ }];
+
+ verifyValues(taken, @[]);
+ });
+
+ it(@"should finish skipping immediately", ^{
+ RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) {
+ return x.integerValue < 0;
+ }];
+
+ verifyValues(taken, values);
+ });
+ });
+
+ describe(@"-combinePreviousWithStart:reduce:", ^{
+ NSArray *values = @[ @1, @2, @3 ];
+ __block RACStream *stream;
+ beforeEach(^{
+ stream = streamWithValues(values);
+ });
+
+ it(@"should pass the previous next into the reduce block", ^{
+ NSMutableArray *previouses = [NSMutableArray array];
+ RACStream *mapped = [stream combinePreviousWithStart:nil reduce:^(id previous, id next) {
+ [previouses addObject:previous ?: RACTupleNil.tupleNil];
+ return next;
+ }];
+
+ verifyValues(mapped, @[ @1, @2, @3 ]);
+
+ NSArray *expected = @[ RACTupleNil.tupleNil, @1, @2 ];
+ expect(previouses).to.equal(expected);
+ });
+
+ it(@"should send the combined value", ^{
+ RACStream *mapped = [stream combinePreviousWithStart:@1 reduce:^(NSNumber *previous, NSNumber *next) {
+ return [NSString stringWithFormat:@"%lu - %lu", (unsigned long)previous.unsignedIntegerValue, (unsigned long)next.unsignedIntegerValue];
+ }];
+
+ verifyValues(mapped, @[ @"1 - 1", @"1 - 2", @"2 - 3" ]);
+ });
+ });
+
+ it(@"should reduce tuples", ^{
+ RACStream *stream = streamWithValues(@[
+ RACTuplePack(@"foo", @"bar"),
+ RACTuplePack(@"buzz", @"baz"),
+ RACTuplePack(@"", @"_")
+ ]);
+
+ RACStream *reduced = [stream reduceEach:^(NSString *a, NSString *b) {
+ return [a stringByAppendingString:b];
+ }];
+
+ verifyValues(reduced, @[ @"foobar", @"buzzbaz", @"_" ]);
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.h
new file mode 100644
index 0000000..962b7eb
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.h
@@ -0,0 +1,23 @@
+//
+// RACSubclassObject.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+
+@interface RACSubclassObject : RACTestObject
+
+// Set whenever -forwardInvocation: is invoked on the receiver.
+@property (nonatomic, assign) SEL forwardedSelector;
+
+// Invokes the superclass implementation with `objectValue` concatenated to
+// "SUBCLASS".
+- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue;
+
+// Asynchronously invokes the superclass implementation on the current scheduler.
+- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.m
new file mode 100644
index 0000000..41e61f7
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.m
@@ -0,0 +1,38 @@
+//
+// RACSubclassObject.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 3/18/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubclassObject.h"
+#import "RACScheduler.h"
+
+@implementation RACSubclassObject
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+ self.forwardedSelector = invocation.selector;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
+ NSParameterAssert(selector != NULL);
+
+ NSMethodSignature *signature = [super methodSignatureForSelector:selector];
+ if (signature != nil) return signature;
+
+ return [super methodSignatureForSelector:@selector(description)];
+}
+
+- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue {
+ NSString *appended = [[objectValue description] stringByAppendingString:@"SUBCLASS"];
+ return [super combineObjectValue:appended andIntegerValue:integerValue];
+}
+
+- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue {
+ [RACScheduler.currentScheduler schedule:^{
+ [super setObjectValue:objectValue andSecondObjectValue:secondObjectValue];
+ }];
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubjectSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubjectSpec.m
new file mode 100644
index 0000000..6bc175f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubjectSpec.m
@@ -0,0 +1,335 @@
+//
+// RACSubjectSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/24/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriberExamples.h"
+
+#import <libkern/OSAtomic.h>
+#import "EXTScope.h"
+#import "RACBehaviorSubject.h"
+#import "RACDisposable.h"
+#import "RACReplaySubject.h"
+#import "RACScheduler.h"
+#import "RACSignal+Operations.h"
+#import "RACSubject.h"
+#import "RACUnit.h"
+
+SpecBegin(RACSubject)
+
+describe(@"RACSubject", ^{
+ __block RACSubject *subject;
+ __block NSMutableArray *values;
+
+ __block BOOL success;
+ __block NSError *error;
+
+ beforeEach(^{
+ values = [NSMutableArray array];
+
+ subject = [RACSubject subject];
+ success = YES;
+ error = nil;
+
+ [subject subscribeNext:^(id value) {
+ [values addObject:value];
+ } error:^(NSError *e) {
+ error = e;
+ success = NO;
+ } completed:^{
+ success = YES;
+ }];
+ });
+
+ itShouldBehaveLike(RACSubscriberExamples, ^{
+ return @{
+ RACSubscriberExampleSubscriber: subject,
+ RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy],
+ RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy],
+ RACSubscriberExampleSuccessBlock: [^{ return success; } copy]
+ };
+ });
+});
+
+describe(@"RACReplaySubject", ^{
+ __block RACReplaySubject *subject = nil;
+
+ describe(@"with a capacity of 1", ^{
+ beforeEach(^{
+ subject = [RACReplaySubject replaySubjectWithCapacity:1];
+ });
+
+ it(@"should send the last value", ^{
+ id firstValue = @"blah";
+ id secondValue = @"more blah";
+
+ [subject sendNext:firstValue];
+ [subject sendNext:secondValue];
+
+ __block id valueReceived = nil;
+ [subject subscribeNext:^(id x) {
+ valueReceived = x;
+ }];
+
+ expect(valueReceived).to.equal(secondValue);
+ });
+
+ it(@"should send the last value to new subscribers after completion", ^{
+ id firstValue = @"blah";
+ id secondValue = @"more blah";
+
+ __block id valueReceived = nil;
+ __block NSUInteger nextsReceived = 0;
+
+ [subject sendNext:firstValue];
+ [subject sendNext:secondValue];
+
+ expect(nextsReceived).to.equal(0);
+ expect(valueReceived).to.beNil();
+
+ [subject sendCompleted];
+
+ [subject subscribeNext:^(id x) {
+ valueReceived = x;
+ nextsReceived++;
+ }];
+
+ expect(nextsReceived).to.equal(1);
+ expect(valueReceived).to.equal(secondValue);
+ });
+
+ it(@"should not send any values to new subscribers if none were sent originally", ^{
+ [subject sendCompleted];
+
+ __block BOOL nextInvoked = NO;
+ [subject subscribeNext:^(id x) {
+ nextInvoked = YES;
+ }];
+
+ expect(nextInvoked).to.beFalsy();
+ });
+
+ it(@"should resend errors", ^{
+ NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil];
+ [subject sendError:error];
+
+ __block BOOL errorSent = NO;
+ [subject subscribeError:^(NSError *sentError) {
+ expect(sentError).to.equal(error);
+ errorSent = YES;
+ }];
+
+ expect(errorSent).to.beTruthy();
+ });
+
+ it(@"should resend nil errors", ^{
+ [subject sendError:nil];
+
+ __block BOOL errorSent = NO;
+ [subject subscribeError:^(NSError *sentError) {
+ expect(sentError).to.beNil();
+ errorSent = YES;
+ }];
+
+ expect(errorSent).to.beTruthy();
+ });
+ });
+
+ describe(@"with an unlimited capacity", ^{
+ beforeEach(^{
+ subject = [RACReplaySubject subject];
+ });
+
+ itShouldBehaveLike(RACSubscriberExamples, ^{
+ return @{
+ RACSubscriberExampleSubscriber: subject,
+ RACSubscriberExampleValuesReceivedBlock: [^{
+ NSMutableArray *values = [NSMutableArray array];
+
+ // This subscription should synchronously dump all values already
+ // received into 'values'.
+ [subject subscribeNext:^(id value) {
+ [values addObject:value];
+ }];
+
+ return values;
+ } copy],
+ RACSubscriberExampleErrorReceivedBlock: [^{
+ __block NSError *error = nil;
+
+ [subject subscribeError:^(NSError *x) {
+ error = x;
+ }];
+
+ return error;
+ } copy],
+ RACSubscriberExampleSuccessBlock: [^{
+ __block BOOL success = YES;
+
+ [subject subscribeError:^(NSError *x) {
+ success = NO;
+ }];
+
+ return success;
+ } copy]
+ };
+ });
+
+ it(@"should send both values to new subscribers after completion", ^{
+ id firstValue = @"blah";
+ id secondValue = @"more blah";
+
+ [subject sendNext:firstValue];
+ [subject sendNext:secondValue];
+ [subject sendCompleted];
+
+ __block BOOL completed = NO;
+ NSMutableArray *valuesReceived = [NSMutableArray array];
+ [subject subscribeNext:^(id x) {
+ [valuesReceived addObject:x];
+ } completed:^{
+ completed = YES;
+ }];
+
+ expect(valuesReceived.count).to.equal(2);
+ NSArray *expected = [NSArray arrayWithObjects:firstValue, secondValue, nil];
+ expect(valuesReceived).to.equal(expected);
+ expect(completed).to.beTruthy();
+ });
+
+ it(@"should send values in the same order live as when replaying", ^{
+ NSUInteger count = 49317;
+
+ // Just leak it, ain't no thang.
+ __unsafe_unretained volatile id *values = (__unsafe_unretained id *)calloc(count, sizeof(*values));
+ __block volatile int32_t nextIndex = 0;
+
+ [subject subscribeNext:^(NSNumber *value) {
+ int32_t indexPlusOne = OSAtomicIncrement32(&nextIndex);
+ values[indexPlusOne - 1] = value;
+ }];
+
+ dispatch_queue_t queue = dispatch_queue_create("com.github.ReactiveCocoa.RACSubjectSpec", DISPATCH_QUEUE_CONCURRENT);
+ @onExit {
+ dispatch_release(queue);
+ };
+
+ dispatch_suspend(queue);
+
+ for (NSUInteger i = 0; i < count; i++) {
+ dispatch_async(queue, ^{
+ [subject sendNext:@(i)];
+ });
+ }
+
+ dispatch_resume(queue);
+ dispatch_barrier_sync(queue, ^{
+ [subject sendCompleted];
+ });
+
+ OSMemoryBarrier();
+
+ NSArray *liveValues = [NSArray arrayWithObjects:(id *)values count:(NSUInteger)nextIndex];
+ expect(liveValues.count).to.equal(count);
+
+ NSArray *replayedValues = subject.toArray;
+ expect(replayedValues.count).to.equal(count);
+
+ // It should return the same ordering for multiple invocations too.
+ expect(replayedValues).to.equal(subject.toArray);
+
+ [replayedValues enumerateObjectsUsingBlock:^(id value, NSUInteger index, BOOL *stop) {
+ expect(liveValues[index]).to.equal(value);
+ }];
+ });
+
+ it(@"should have a current scheduler when replaying", ^{
+ [subject sendNext:RACUnit.defaultUnit];
+
+ __block RACScheduler *currentScheduler;
+ [subject subscribeNext:^(id x) {
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+
+ expect(currentScheduler).notTo.beNil();
+
+ currentScheduler = nil;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [subject subscribeNext:^(id x) {
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+ });
+
+ expect(currentScheduler).willNot.beNil();
+ });
+
+ it(@"should stop replaying when the subscription is disposed", ^{
+ NSMutableArray *values = [NSMutableArray array];
+
+ [subject sendNext:@0];
+ [subject sendNext:@1];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ __block RACDisposable *disposable = [subject subscribeNext:^(id x) {
+ expect(disposable).notTo.beNil();
+
+ [values addObject:x];
+ [disposable dispose];
+ }];
+ });
+
+ expect(values).will.equal(@[ @0 ]);
+ });
+
+ it(@"should finish replaying before completing", ^{
+ [subject sendNext:@1];
+
+ __block id received;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [subject subscribeNext:^(id x) {
+ received = x;
+ }];
+
+ [subject sendCompleted];
+ });
+
+ expect(received).will.equal(@1);
+ });
+
+ it(@"should finish replaying before erroring", ^{
+ [subject sendNext:@1];
+
+ __block id received;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [subject subscribeNext:^(id x) {
+ received = x;
+ }];
+
+ [subject sendError:[NSError errorWithDomain:@"blah" code:-99 userInfo:nil]];
+ });
+
+ expect(received).will.equal(@1);
+ });
+
+ it(@"should finish replaying before sending new values", ^{
+ [subject sendNext:@1];
+
+ NSMutableArray *received = [NSMutableArray array];
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [subject subscribeNext:^(id x) {
+ [received addObject:x];
+ }];
+
+ [subject sendNext:@2];
+ });
+
+ NSArray *expected = @[ @1, @2 ];
+ expect(received).will.equal(expected);
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.h
new file mode 100644
index 0000000..edc9e5a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.h
@@ -0,0 +1,23 @@
+//
+// RACSubscriberExamples.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-27.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+// The name of the shared examples for implementors of <RACSubscriber>.
+extern NSString * const RACSubscriberExamples;
+
+// id<RACSubscriber>
+extern NSString * const RACSubscriberExampleSubscriber;
+
+// A block which returns an NSArray of the values received so far.
+extern NSString * const RACSubscriberExampleValuesReceivedBlock;
+
+// A block which returns any NSError received so far.
+extern NSString * const RACSubscriberExampleErrorReceivedBlock;
+
+// A block which returns a BOOL indicating whether the subscriber is successful
+// so far.
+extern NSString * const RACSubscriberExampleSuccessBlock;
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.m
new file mode 100644
index 0000000..946a307
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.m
@@ -0,0 +1,185 @@
+//
+// RACSubscriberExamples.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-27.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriberExamples.h"
+
+#import "NSObject+RACDeallocating.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSubject.h"
+#import "RACSubscriber.h"
+
+NSString * const RACSubscriberExamples = @"RACSubscriberExamples";
+NSString * const RACSubscriberExampleSubscriber = @"RACSubscriberExampleSubscriber";
+NSString * const RACSubscriberExampleValuesReceivedBlock = @"RACSubscriberExampleValuesReceivedBlock";
+NSString * const RACSubscriberExampleErrorReceivedBlock = @"RACSubscriberExampleErrorReceivedBlock";
+NSString * const RACSubscriberExampleSuccessBlock = @"RACSubscriberExampleSuccessBlock";
+
+SharedExampleGroupsBegin(RACSubscriberExamples)
+
+sharedExamplesFor(RACSubscriberExamples, ^(NSDictionary *data) {
+ __block NSArray * (^valuesReceived)(void);
+ __block NSError * (^errorReceived)(void);
+ __block BOOL (^success)(void);
+ __block id<RACSubscriber> subscriber;
+
+ beforeEach(^{
+ valuesReceived = data[RACSubscriberExampleValuesReceivedBlock];
+ errorReceived = data[RACSubscriberExampleErrorReceivedBlock];
+ success = data[RACSubscriberExampleSuccessBlock];
+ subscriber = data[RACSubscriberExampleSubscriber];
+ expect(subscriber).notTo.beNil();
+ });
+
+ it(@"should accept a nil error", ^{
+ [subscriber sendError:nil];
+
+ expect(success()).to.beFalsy();
+ expect(errorReceived()).to.beNil();
+ expect(valuesReceived()).to.equal(@[]);
+ });
+
+ describe(@"with values", ^{
+ __block NSSet *values;
+
+ beforeEach(^{
+ NSMutableSet *mutableValues = [NSMutableSet set];
+ for (NSUInteger i = 0; i < 20; i++) {
+ [mutableValues addObject:@(i)];
+ }
+
+ values = [mutableValues copy];
+ });
+
+ it(@"should send nexts serially, even when delivered from multiple threads", ^{
+ NSArray *allValues = values.allObjects;
+ dispatch_apply(allValues.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [^(size_t index) {
+ [subscriber sendNext:allValues[index]];
+ } copy]);
+
+ expect(success()).to.beTruthy();
+ expect(errorReceived()).to.beNil();
+
+ NSSet *valuesReceivedSet = [NSSet setWithArray:valuesReceived()];
+ expect(valuesReceivedSet).to.equal(values);
+ });
+ });
+
+ describe(@"multiple subscriptions", ^{
+ __block RACSubject *first;
+ __block RACSubject *second;
+
+ beforeEach(^{
+ first = [RACSubject subject];
+ [first subscribe:subscriber];
+
+ second = [RACSubject subject];
+ [second subscribe:subscriber];
+ });
+
+ it(@"should send values from all subscriptions", ^{
+ [first sendNext:@"foo"];
+ [second sendNext:@"bar"];
+ [first sendNext:@"buzz"];
+ [second sendNext:@"baz"];
+
+ expect(success()).to.beTruthy();
+ expect(errorReceived()).to.beNil();
+
+ NSArray *expected = @[ @"foo", @"bar", @"buzz", @"baz" ];
+ expect(valuesReceived()).to.equal(expected);
+ });
+
+ it(@"should terminate after the first error from any subscription", ^{
+ NSError *error = [NSError errorWithDomain:@"" code:-1 userInfo:nil];
+
+ [first sendNext:@"foo"];
+ [second sendError:error];
+ [first sendNext:@"buzz"];
+
+ expect(success()).to.beFalsy();
+ expect(errorReceived()).to.equal(error);
+
+ NSArray *expected = @[ @"foo" ];
+ expect(valuesReceived()).to.equal(expected);
+ });
+
+ it(@"should terminate after the first completed from any subscription", ^{
+ [first sendNext:@"foo"];
+ [second sendNext:@"bar"];
+ [first sendCompleted];
+ [second sendNext:@"baz"];
+
+ expect(success()).to.beTruthy();
+ expect(errorReceived()).to.beNil();
+
+ NSArray *expected = @[ @"foo", @"bar" ];
+ expect(valuesReceived()).to.equal(expected);
+ });
+
+ it(@"should dispose of all current subscriptions upon termination", ^{
+ __block BOOL firstDisposed = NO;
+ RACSignal *firstDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ firstDisposed = YES;
+ }];
+ }];
+
+ __block BOOL secondDisposed = NO;
+ RACSignal *secondDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ secondDisposed = YES;
+ }];
+ }];
+
+ [firstDisposableSignal subscribe:subscriber];
+ [secondDisposableSignal subscribe:subscriber];
+
+ expect(firstDisposed).to.beFalsy();
+ expect(secondDisposed).to.beFalsy();
+
+ [first sendCompleted];
+
+ expect(firstDisposed).to.beTruthy();
+ expect(secondDisposed).to.beTruthy();
+ });
+
+ it(@"should dispose of future subscriptions upon termination", ^{
+ __block BOOL disposed = NO;
+ RACSignal *disposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
+ return [RACDisposable disposableWithBlock:^{
+ disposed = YES;
+ }];
+ }];
+
+ [first sendCompleted];
+ expect(disposed).to.beFalsy();
+
+ [disposableSignal subscribe:subscriber];
+ expect(disposed).to.beTruthy();
+ });
+ });
+
+ describe(@"memory management", ^{
+ it(@"should not retain disposed disposables", ^{
+ __block BOOL disposableDeallocd = NO;
+ @autoreleasepool {
+ RACCompoundDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACCompoundDisposable disposableWithBlock:^{}];
+ [disposable.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ disposableDeallocd = YES;
+ }]];
+
+ [subscriber didSubscribeWithDisposable:disposable];
+ [disposable dispose];
+ }
+ expect(disposableDeallocd).to.beTruthy();
+ });
+ });
+});
+
+SharedExampleGroupsEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberSpec.m
new file mode 100644
index 0000000..8a1725d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberSpec.m
@@ -0,0 +1,130 @@
+//
+// RACSubscriberSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-27.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriberExamples.h"
+
+#import "RACSubscriber.h"
+#import "RACSubscriber+Private.h"
+#import <libkern/OSAtomic.h>
+
+SpecBegin(RACSubscriber)
+
+__block RACSubscriber *subscriber;
+__block NSMutableArray *values;
+
+__block volatile BOOL finished;
+__block volatile int32_t nextsAfterFinished;
+
+__block BOOL success;
+__block NSError *error;
+
+beforeEach(^{
+ values = [NSMutableArray array];
+
+ finished = NO;
+ nextsAfterFinished = 0;
+
+ success = YES;
+ error = nil;
+
+ subscriber = [RACSubscriber subscriberWithNext:^(id value) {
+ if (finished) OSAtomicIncrement32Barrier(&nextsAfterFinished);
+
+ [values addObject:value];
+ } error:^(NSError *e) {
+ error = e;
+ success = NO;
+ } completed:^{
+ success = YES;
+ }];
+});
+
+itShouldBehaveLike(RACSubscriberExamples, ^{
+ return @{
+ RACSubscriberExampleSubscriber: subscriber,
+ RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy],
+ RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy],
+ RACSubscriberExampleSuccessBlock: [^{ return success; } copy]
+ };
+});
+
+describe(@"finishing", ^{
+ __block void (^sendValues)(void);
+ __block BOOL expectedSuccess;
+
+ __block dispatch_group_t dispatchGroup;
+ __block dispatch_queue_t concurrentQueue;
+
+ beforeEach(^{
+ dispatchGroup = dispatch_group_create();
+ expect(dispatchGroup).notTo.beNil();
+
+ concurrentQueue = dispatch_queue_create("com.github.ReactiveCocoa.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT);
+ expect(concurrentQueue).notTo.beNil();
+
+ dispatch_suspend(concurrentQueue);
+
+ sendValues = [^{
+ for (NSUInteger i = 0; i < 15; i++) {
+ dispatch_group_async(dispatchGroup, concurrentQueue, ^{
+ [subscriber sendNext:@(i)];
+ });
+ }
+ } copy];
+
+ sendValues();
+ });
+
+ afterEach(^{
+ sendValues();
+ dispatch_resume(concurrentQueue);
+
+ // Time out after one second.
+ dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
+ expect(dispatch_group_wait(dispatchGroup, time)).to.equal(0);
+
+ dispatch_release(dispatchGroup);
+ dispatchGroup = NULL;
+
+ dispatch_release(concurrentQueue);
+ concurrentQueue = NULL;
+
+ expect(nextsAfterFinished).to.equal(0);
+
+ if (expectedSuccess) {
+ expect(success).to.beTruthy();
+ expect(error).to.beNil();
+ } else {
+ expect(success).to.beFalsy();
+ }
+ });
+
+ it(@"should never invoke next after sending completed", ^{
+ expectedSuccess = YES;
+
+ dispatch_group_async(dispatchGroup, concurrentQueue, ^{
+ [subscriber sendCompleted];
+
+ finished = YES;
+ OSMemoryBarrier();
+ });
+ });
+
+ it(@"should never invoke next after sending error", ^{
+ expectedSuccess = NO;
+
+ dispatch_group_async(dispatchGroup, concurrentQueue, ^{
+ [subscriber sendError:nil];
+
+ finished = YES;
+ OSMemoryBarrier();
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriptingAssignmentTrampolineSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriptingAssignmentTrampolineSpec.m
new file mode 100644
index 0000000..9165f11
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriptingAssignmentTrampolineSpec.m
@@ -0,0 +1,33 @@
+//
+// RACSubscriptingAssignmentTrampolineSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/24/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSubscriptingAssignmentTrampoline.h"
+#import "RACPropertySignalExamples.h"
+#import "RACTestObject.h"
+#import "RACSubject.h"
+
+SpecBegin(RACSubscriptingAssignmentTrampoline)
+
+id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) {
+ [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:testObject nilValue:nilValue][keyPath] = signal;
+};
+
+itShouldBehaveLike(RACPropertySignalExamples, ^{
+ return @{ RACPropertySignalExamplesSetupBlock: setupBlock };
+});
+
+it(@"should expand the RAC macro properly", ^{
+ RACSubject *subject = [RACSubject subject];
+ RACTestObject *testObject = [[RACTestObject alloc] init];
+ RAC(testObject, objectValue) = subject;
+
+ [subject sendNext:@1];
+ expect(testObject.objectValue).to.equal(@1);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTargetQueueSchedulerSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTargetQueueSchedulerSpec.m
new file mode 100644
index 0000000..b193844
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTargetQueueSchedulerSpec.m
@@ -0,0 +1,50 @@
+//
+// RACTargetQueueSchedulerSpec.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/7/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTargetQueueScheduler.h"
+#import <libkern/OSAtomic.h>
+
+SpecBegin(RACTargetQueueScheduler)
+
+it(@"should have a valid current scheduler", ^{
+ dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_SERIAL);
+ RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue];
+ __block RACScheduler *currentScheduler;
+ [scheduler schedule:^{
+ currentScheduler = RACScheduler.currentScheduler;
+ }];
+
+ expect(currentScheduler).will.equal(scheduler);
+
+ dispatch_release(queue);
+});
+
+it(@"should schedule blocks FIFO even when given a concurrent queue", ^{
+ dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_CONCURRENT);
+ RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue];
+ __block volatile int32_t startedCount = 0;
+ __block volatile uint32_t waitInFirst = 1;
+ [scheduler schedule:^{
+ OSAtomicIncrement32Barrier(&startedCount);
+ while (waitInFirst == 1) ;
+ }];
+
+ [scheduler schedule:^{
+ OSAtomicIncrement32Barrier(&startedCount);
+ }];
+
+ expect(startedCount).will.equal(1);
+
+ OSAtomicAnd32Barrier(0, &waitInFirst);
+
+ expect(startedCount).will.equal(2);
+
+ dispatch_release(queue);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.h
new file mode 100644
index 0000000..0470d98
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.h
@@ -0,0 +1,15 @@
+//
+// RACTestExampleScheduler.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/7/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <ReactiveCocoa/ReactiveCocoa.h>
+
+@interface RACTestExampleScheduler : RACQueueScheduler
+
+- (id)initWithQueue:(dispatch_queue_t)queue;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.m
new file mode 100644
index 0000000..859055c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestExampleScheduler.m
@@ -0,0 +1,39 @@
+//
+// RACTestExampleScheduler.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 6/7/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestExampleScheduler.h"
+#import "RACQueueScheduler+Subclass.h"
+
+@implementation RACTestExampleScheduler
+
+#pragma mark Lifecycle
+
+- (id)initWithQueue:(dispatch_queue_t)queue {
+ return [super initWithName:nil queue:queue];
+}
+
+#pragma mark RACScheduler
+
+- (RACDisposable *)schedule:(void (^)(void))block {
+ dispatch_async(self.queue, ^{
+ [self performAsCurrentScheduler:block];
+ });
+
+ return nil;
+}
+
+- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
+ dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)([date timeIntervalSinceNow] * NSEC_PER_SEC));
+ dispatch_after(when, self.queue, ^{
+ [self performAsCurrentScheduler:block];
+ });
+
+ return nil;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.h
new file mode 100644
index 0000000..a963b0a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.h
@@ -0,0 +1,86 @@
+//
+// RACTestObject.h
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/18/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef struct {
+ long long integerField;
+ double doubleField;
+} RACTestStruct;
+
+@protocol RACTestProtocol <NSObject>
+
+@optional
+- (void)optionalProtocolMethodWithObjectValue:(id)objectValue;
+
+@end
+
+@interface RACTestObject : NSObject <RACTestProtocol>
+
+@property (nonatomic, strong) id objectValue;
+@property (nonatomic, strong) id secondObjectValue;
+@property (nonatomic, strong) RACTestObject *strongTestObjectValue;
+@property (nonatomic, weak) RACTestObject *weakTestObjectValue;
+@property (nonatomic, weak) id<RACTestProtocol> weakObjectWithProtocol;
+@property (nonatomic, assign) NSInteger integerValue;
+// Holds a copy of the string.
+@property (nonatomic, assign) char *charPointerValue;
+// Holds a copy of the string.
+@property (nonatomic, assign) const char *constCharPointerValue;
+@property (nonatomic, assign) CGRect rectValue;
+@property (nonatomic, assign) CGSize sizeValue;
+@property (nonatomic, assign) CGPoint pointValue;
+@property (nonatomic, assign) NSRange rangeValue;
+@property (nonatomic, assign) RACTestStruct structValue;
+@property (nonatomic, assign) _Bool c99BoolValue;
+@property (nonatomic, copy) NSString *stringValue;
+@property (nonatomic, copy) NSArray *arrayValue;
+@property (nonatomic, copy) NSSet *setValue;
+@property (nonatomic, copy) NSOrderedSet *orderedSetValue;
+@property (nonatomic, strong) id slowObjectValue;
+
+// Returns a new object each time, with the integerValue set to 42.
+@property (nonatomic, copy, readonly) RACTestObject *dynamicObjectProperty;
+
+// Returns a new object each time, with the integerValue set to 42.
+- (RACTestObject *)dynamicObjectMethod;
+
+// Whether to allow -setNilValueForKey: to be invoked without throwing an
+// exception.
+@property (nonatomic, assign) BOOL catchSetNilValueForKey;
+
+// Has -setObjectValue:andIntegerValue: been called?
+@property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndIntegerValue;
+
+// Has -setObjectValue:andSecondObjectValue: been called?
+@property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndSecondObjectValue;
+
+- (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue;
+- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue;
+
+// Returns a string of the form "objectValue: integerValue".
+- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue;
+- (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue;
+
+- (void)lifeIsGood:(id)sender;
+
++ (void)lifeIsGood:(id)sender;
+
+- (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue;
+
+// Writes 5 to the int pointed to by intPointer.
+- (void)write5ToIntPointer:(int *)intPointer;
+
+- (NSInteger)doubleInteger:(NSInteger)integer;
+- (char *)doubleString:(char *)string;
+- (const char *)doubleConstString:(const char *)string;
+- (RACTestStruct)doubleStruct:(RACTestStruct)testStruct;
+
+- (dispatch_block_t)wrapBlock:(dispatch_block_t)block;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.m
new file mode 100644
index 0000000..f590703
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.m
@@ -0,0 +1,121 @@
+//
+// RACTestObject.m
+// ReactiveCocoa
+//
+// Created by Josh Abernathy on 9/18/12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestObject.h"
+
+@implementation RACTestObject
+
+- (void)dealloc {
+ free(_charPointerValue);
+ free((void *)_constCharPointerValue);
+}
+
+- (void)setNilValueForKey:(NSString *)key {
+ if (!self.catchSetNilValueForKey) [super setNilValueForKey:key];
+}
+
+- (void)setCharPointerValue:(char *)charPointerValue {
+ if (charPointerValue == _charPointerValue) return;
+ free(_charPointerValue);
+ _charPointerValue = strdup(charPointerValue);
+}
+
+- (void)setConstCharPointerValue:(const char *)constCharPointerValue {
+ if (constCharPointerValue == _constCharPointerValue) return;
+ free((void *)_constCharPointerValue);
+ _constCharPointerValue = strdup(constCharPointerValue);
+}
+
+- (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue {
+ self.hasInvokedSetObjectValueAndIntegerValue = YES;
+ self.objectValue = objectValue;
+ self.integerValue = integerValue;
+}
+
+- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue {
+ self.hasInvokedSetObjectValueAndSecondObjectValue = YES;
+ self.objectValue = objectValue;
+ self.secondObjectValue = secondObjectValue;
+}
+
+- (void)setSlowObjectValue:(id)value {
+ [NSThread sleepForTimeInterval:0.02];
+ _slowObjectValue = value;
+}
+
+- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue {
+ return [NSString stringWithFormat:@"%@: %ld", objectValue, (long)integerValue];
+}
+
+- (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue {
+ return [NSString stringWithFormat:@"%@: %@", objectValue, secondObjectValue];
+}
+
+- (void)lifeIsGood:(id)sender {
+
+}
+
++ (void)lifeIsGood:(id)sender {
+
+}
+
+- (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue {
+ return NSMakeRange((NSUInteger)[objectValue integerValue], (NSUInteger)integerValue);
+}
+
+- (RACTestObject *)dynamicObjectProperty {
+ return [self dynamicObjectMethod];
+}
+
+- (RACTestObject *)dynamicObjectMethod {
+ RACTestObject *testObject = [[RACTestObject alloc] init];
+ testObject.integerValue = 42;
+ return testObject;
+}
+
+- (void)write5ToIntPointer:(int *)intPointer {
+ NSCParameterAssert(intPointer != NULL);
+ *intPointer = 5;
+}
+
+- (NSInteger)doubleInteger:(NSInteger)integer {
+ return integer * 2;
+}
+
+- (char *)doubleString:(char *)string {
+ size_t doubledSize = strlen(string) * 2 + 1;
+ char *doubledString = malloc(sizeof(char) * doubledSize);
+
+ doubledString[0] = '\0';
+ strlcat(doubledString, string, doubledSize);
+ strlcat(doubledString, string, doubledSize);
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ free(doubledString);
+ });
+
+ return doubledString;
+}
+
+- (const char *)doubleConstString:(const char *)string {
+ return [self doubleString:(char *)string];
+}
+
+- (RACTestStruct)doubleStruct:(RACTestStruct)testStruct {
+ testStruct.integerField *= 2;
+ testStruct.doubleField *= 2;
+ return testStruct;
+}
+
+- (dispatch_block_t)wrapBlock:(dispatch_block_t)block {
+ return ^{
+ block();
+ };
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestSchedulerSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestSchedulerSpec.m
new file mode 100644
index 0000000..baa8994
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestSchedulerSpec.m
@@ -0,0 +1,175 @@
+//
+// RACTestSchedulerSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestScheduler.h"
+
+SpecBegin(RACTestScheduler)
+
+__block RACTestScheduler *scheduler;
+
+beforeEach(^{
+ scheduler = [[RACTestScheduler alloc] init];
+ expect(scheduler).notTo.beNil();
+});
+
+it(@"should do nothing when stepping while empty", ^{
+ [scheduler step];
+ [scheduler step:5];
+ [scheduler stepAll];
+});
+
+it(@"should execute the earliest enqueued block when stepping", ^{
+ __block BOOL firstExecuted = NO;
+ [scheduler schedule:^{
+ firstExecuted = YES;
+ }];
+
+ __block BOOL secondExecuted = NO;
+ [scheduler schedule:^{
+ secondExecuted = YES;
+ }];
+
+ expect(firstExecuted).to.beFalsy();
+ expect(secondExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(firstExecuted).to.beTruthy();
+ expect(secondExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(secondExecuted).to.beTruthy();
+});
+
+it(@"should step multiple times", ^{
+ __block BOOL firstExecuted = NO;
+ [scheduler schedule:^{
+ firstExecuted = YES;
+ }];
+
+ __block BOOL secondExecuted = NO;
+ [scheduler schedule:^{
+ secondExecuted = YES;
+ }];
+
+ __block BOOL thirdExecuted = NO;
+ [scheduler schedule:^{
+ thirdExecuted = YES;
+ }];
+
+ expect(firstExecuted).to.beFalsy();
+ expect(secondExecuted).to.beFalsy();
+ expect(thirdExecuted).to.beFalsy();
+
+ [scheduler step:2];
+ expect(firstExecuted).to.beTruthy();
+ expect(secondExecuted).to.beTruthy();
+ expect(thirdExecuted).to.beFalsy();
+
+ [scheduler step:1];
+ expect(thirdExecuted).to.beTruthy();
+});
+
+it(@"should step through all scheduled blocks", ^{
+ __block NSUInteger executions = 0;
+ for (NSUInteger i = 0; i < 10; i++) {
+ [scheduler schedule:^{
+ executions++;
+ }];
+ }
+
+ expect(executions).to.equal(0);
+
+ [scheduler stepAll];
+ expect(executions).to.equal(10);
+});
+
+it(@"should execute blocks in date order when stepping", ^{
+ __block BOOL laterExecuted = NO;
+ [scheduler after:[NSDate distantFuture] schedule:^{
+ laterExecuted = YES;
+ }];
+
+ __block BOOL earlierExecuted = NO;
+ [scheduler after:[NSDate dateWithTimeIntervalSinceNow:20] schedule:^{
+ earlierExecuted = YES;
+ }];
+
+ expect(earlierExecuted).to.beFalsy();
+ expect(laterExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(earlierExecuted).to.beTruthy();
+ expect(laterExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(laterExecuted).to.beTruthy();
+});
+
+it(@"should execute delayed blocks in date order when stepping", ^{
+ __block BOOL laterExecuted = NO;
+ [scheduler afterDelay:100 schedule:^{
+ laterExecuted = YES;
+ }];
+
+ __block BOOL earlierExecuted = NO;
+ [scheduler afterDelay:50 schedule:^{
+ earlierExecuted = YES;
+ }];
+
+ expect(earlierExecuted).to.beFalsy();
+ expect(laterExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(earlierExecuted).to.beTruthy();
+ expect(laterExecuted).to.beFalsy();
+
+ [scheduler step];
+ expect(laterExecuted).to.beTruthy();
+});
+
+it(@"should execute a repeating blocks in date order", ^{
+ __block NSUInteger firstExecutions = 0;
+ [scheduler after:[NSDate dateWithTimeIntervalSinceNow:20] repeatingEvery:5 withLeeway:0 schedule:^{
+ firstExecutions++;
+ }];
+
+ __block NSUInteger secondExecutions = 0;
+ [scheduler after:[NSDate dateWithTimeIntervalSinceNow:22] repeatingEvery:10 withLeeway:0 schedule:^{
+ secondExecutions++;
+ }];
+
+ expect(firstExecutions).to.equal(0);
+ expect(secondExecutions).to.equal(0);
+
+ // 20 ticks
+ [scheduler step];
+ expect(firstExecutions).to.equal(1);
+ expect(secondExecutions).to.equal(0);
+
+ // 22 ticks
+ [scheduler step];
+ expect(firstExecutions).to.equal(1);
+ expect(secondExecutions).to.equal(1);
+
+ // 25 ticks
+ [scheduler step];
+ expect(firstExecutions).to.equal(2);
+ expect(secondExecutions).to.equal(1);
+
+ // 30 ticks
+ [scheduler step];
+ expect(firstExecutions).to.equal(3);
+ expect(secondExecutions).to.equal(1);
+
+ // 32 ticks
+ [scheduler step];
+ expect(firstExecutions).to.equal(3);
+ expect(secondExecutions).to.equal(2);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.h b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.h
new file mode 100644
index 0000000..03e9716
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.h
@@ -0,0 +1,16 @@
+//
+// RACTestUIButton.h
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+// Enables use of -sendActionsForControlEvents: in unit tests.
+@interface RACTestUIButton : UIButton
+
++ (instancetype)button;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.m
new file mode 100644
index 0000000..48b2674
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTestUIButton.m
@@ -0,0 +1,27 @@
+//
+// RACTestUIButton.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestUIButton.h"
+
+@implementation RACTestUIButton
+
++ (instancetype)button {
+ RACTestUIButton *button = [self buttonWithType:UIButtonTypeCustom];
+ return button;
+}
+
+// Required for unit testing – controls don't work normally
+// outside of normal apps.
+-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [target performSelector:action withObject:self];
+#pragma clang diagnostic pop
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/RACTupleSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTupleSpec.m
new file mode 100644
index 0000000..e85d76d
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/RACTupleSpec.m
@@ -0,0 +1,120 @@
+//
+// RACTupleSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-12-12.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTuple.h"
+#import "RACUnit.h"
+
+SpecBegin(RACTuple)
+
+describe(@"RACTupleUnpack", ^{
+ it(@"should unpack a single value", ^{
+ RACTupleUnpack(RACUnit *value) = [RACTuple tupleWithObjects:RACUnit.defaultUnit, nil];
+ expect(value).to.equal(RACUnit.defaultUnit);
+ });
+
+ it(@"should translate RACTupleNil", ^{
+ RACTupleUnpack(id value) = [RACTuple tupleWithObjects:RACTupleNil.tupleNil, nil];
+ expect(value).to.beNil();
+ });
+
+ it(@"should unpack multiple values", ^{
+ RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", @5, nil];
+
+ expect(str).to.equal(@"foobar");
+ expect(num).to.equal(@5);
+ });
+
+ it(@"should fill in missing values with nil", ^{
+ RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", nil];
+
+ expect(str).to.equal(@"foobar");
+ expect(num).to.beNil();
+ });
+
+ it(@"should skip any values not assigned to", ^{
+ RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", @5, RACUnit.defaultUnit, nil];
+
+ expect(str).to.equal(@"foobar");
+ expect(num).to.equal(@5);
+ });
+
+ it(@"should keep an unpacked value alive when captured in a block", ^{
+ __weak id weakPtr = nil;
+ id (^block)(void) = nil;
+
+ @autoreleasepool {
+ RACTupleUnpack(NSString *str) = [RACTuple tupleWithObjects:[[NSMutableString alloc] init], nil];
+
+ weakPtr = str;
+ expect(weakPtr).notTo.beNil();
+
+ block = [^{
+ return str;
+ } copy];
+ }
+
+ expect(weakPtr).notTo.beNil();
+ expect(block()).to.equal(weakPtr);
+ });
+});
+
+describe(@"RACTuplePack", ^{
+ it(@"should pack a single value", ^{
+ RACTuple *tuple = [RACTuple tupleWithObjects:RACUnit.defaultUnit, nil];
+ expect(RACTuplePack(RACUnit.defaultUnit)).to.equal(tuple);
+ });
+
+ it(@"should translate nil", ^{
+ RACTuple *tuple = [RACTuple tupleWithObjects:RACTupleNil.tupleNil, nil];
+ expect(RACTuplePack(nil)).to.equal(tuple);
+ });
+
+ it(@"should pack multiple values", ^{
+ NSString *string = @"foobar";
+ NSNumber *number = @5;
+ RACTuple *tuple = [RACTuple tupleWithObjects:string, number, nil];
+ expect(RACTuplePack(string, number)).to.equal(tuple);
+ });
+});
+
+describe(@"-tupleByAddingObject:", ^{
+ __block RACTuple *tuple;
+
+ beforeEach(^{
+ tuple = RACTuplePack(@"foo", nil, @"bar");
+ });
+
+ it(@"should add a non-nil object", ^{
+ RACTuple *newTuple = [tuple tupleByAddingObject:@"buzz"];
+ expect(newTuple.count).to.equal(4);
+ expect(newTuple[0]).to.equal(@"foo");
+ expect(newTuple[1]).to.beNil();
+ expect(newTuple[2]).to.equal(@"bar");
+ expect(newTuple[3]).to.equal(@"buzz");
+ });
+
+ it(@"should add nil", ^{
+ RACTuple *newTuple = [tuple tupleByAddingObject:nil];
+ expect(newTuple.count).to.equal(4);
+ expect(newTuple[0]).to.equal(@"foo");
+ expect(newTuple[1]).to.beNil();
+ expect(newTuple[2]).to.equal(@"bar");
+ expect(newTuple[3]).to.beNil();
+ });
+
+ it(@"should add NSNull", ^{
+ RACTuple *newTuple = [tuple tupleByAddingObject:NSNull.null];
+ expect(newTuple.count).to.equal(4);
+ expect(newTuple[0]).to.equal(@"foo");
+ expect(newTuple[1]).to.beNil();
+ expect(newTuple[2]).to.equal(@"bar");
+ expect(newTuple[3]).to.equal(NSNull.null);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Info.plist b/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Info.plist
new file mode 100644
index 0000000..d783a87
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.github.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch b/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch
new file mode 100644
index 0000000..970a36e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch
@@ -0,0 +1,14 @@
+//
+// ReactiveCocoaTests-Prefix.pch
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2012-11-29.
+// Copyright (c) 2012 GitHub, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CGGeometry.h>
+
+#define EXP_SHORTHAND
+#import "Specta.h"
+#import "Expecta.h"
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIActionSheetRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIActionSheetRACSupportSpec.m
new file mode 100644
index 0000000..a695ea5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIActionSheetRACSupportSpec.m
@@ -0,0 +1,36 @@
+//
+// UIActionSheetRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "UIActionSheet+RACSignalSupport.h"
+
+SpecBegin(UIActionSheetRACSupportSpec)
+
+describe(@"-rac_buttonClickedSignal", ^{
+ __block UIActionSheet *actionSheet;
+
+ beforeEach(^{
+ actionSheet = [[UIActionSheet alloc] init];
+ [actionSheet addButtonWithTitle:@"Button 0"];
+ [actionSheet addButtonWithTitle:@"Button 1"];
+ expect(actionSheet).notTo.beNil();
+ });
+
+ it(@"should send the index of the clicked button", ^{
+ __block NSNumber *index = nil;
+ [actionSheet.rac_buttonClickedSignal subscribeNext:^(NSNumber *i) {
+ index = i;
+ }];
+
+ [actionSheet.delegate actionSheet:actionSheet clickedButtonAtIndex:1];
+ expect(index).to.equal(@1);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIAlertViewRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIAlertViewRACSupportSpec.m
new file mode 100644
index 0000000..f37294e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIAlertViewRACSupportSpec.m
@@ -0,0 +1,34 @@
+//
+// UIAlertViewRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Henrik Hodne on 6/16/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <objc/message.h>
+#import "RACSignal.h"
+#import "UIAlertView+RACSignalSupport.h"
+
+SpecBegin(UIAlertViewRACSupport)
+
+describe(@"UIAlertView", ^{
+ __block UIAlertView *alertView;
+
+ beforeEach(^{
+ alertView = [[UIAlertView alloc] initWithFrame:CGRectZero];
+ expect(alertView).notTo.beNil();
+ });
+
+ it(@"sends the index of the clicked button to the buttonClickedSignal when a button is clicked", ^{
+ __block NSInteger index = -1;
+ [alertView.rac_buttonClickedSignal subscribeNext:^(NSNumber *sentIndex) {
+ index = sentIndex.integerValue;
+ }];
+
+ [alertView.delegate alertView:alertView clickedButtonAtIndex:2];
+ expect(index).to.equal(2);
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIBarButtonItemRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIBarButtonItemRACSupportSpec.m
new file mode 100644
index 0000000..089630b
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIBarButtonItemRACSupportSpec.m
@@ -0,0 +1,40 @@
+//
+// UIBarButtonItemRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Kyle LeNeau on 4/13/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACControlCommandExamples.h"
+
+#import "UIBarButtonItem+RACCommandSupport.h"
+#import "RACCommand.h"
+#import "RACDisposable.h"
+
+SpecBegin(UIBarButtonItemRACSupport)
+
+describe(@"UIBarButtonItem", ^{
+ __block UIBarButtonItem *button;
+
+ beforeEach(^{
+ button = [[UIBarButtonItem alloc] init];
+ expect(button).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACControlCommandExamples, ^{
+ return @{
+ RACControlCommandExampleControl: button,
+ RACControlCommandExampleActivateBlock: ^(UIBarButtonItem *button) {
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[button.target methodSignatureForSelector:button.action]];
+ invocation.selector = button.action;
+
+ id target = button.target;
+ [invocation setArgument:&target atIndex:2];
+ [invocation invokeWithTarget:target];
+ }
+ };
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIButtonRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIButtonRACSupportSpec.m
new file mode 100644
index 0000000..0fefa92
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIButtonRACSupportSpec.m
@@ -0,0 +1,39 @@
+//
+// UIButtonRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Ash Furrow on 2013-06-06.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACControlCommandExamples.h"
+#import "RACTestUIButton.h"
+
+#import "UIButton+RACCommandSupport.h"
+#import "RACCommand.h"
+#import "RACDisposable.h"
+
+SpecBegin(UIButtonRACSupport)
+
+describe(@"UIButton", ^{
+ __block UIButton *button;
+
+ beforeEach(^{
+ button = [RACTestUIButton button];
+ expect(button).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACControlCommandExamples, ^{
+ return @{
+ RACControlCommandExampleControl: button,
+ RACControlCommandExampleActivateBlock: ^(UIButton *button) {
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [button sendActionsForControlEvents:UIControlEventTouchUpInside];
+ #pragma clang diagnostic pop
+ }
+ };
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIControlRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIControlRACSupportSpec.m
new file mode 100644
index 0000000..4aaf68c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIControlRACSupportSpec.m
@@ -0,0 +1,105 @@
+//
+// UIControlRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-15.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestUIButton.h"
+
+#import "EXTKeyPathCoding.h"
+#import "NSObject+RACDeallocating.h"
+#import "RACChannelExamples.h"
+#import "RACCompoundDisposable.h"
+#import "RACDisposable.h"
+#import "RACSignal.h"
+#import "UIControl+RACSignalSupport.h"
+#import "UISlider+RACSignalSupport.h"
+
+SpecBegin(UIControlRACSupport)
+
+void (^setViewValueBlock)(UISlider *, NSNumber *) = ^(UISlider *view, NSNumber *value) {
+ view.value = value.floatValue;
+
+ // UIControlEvents don't trigger from programmatic modification. Do it
+ // manually.
+ for (id target in view.allTargets) {
+ // Control events are a mask, but UIControlEventAllEvents doesn't seem to
+ // match anything, 0 does.
+ for (NSString *selectorString in [view actionsForTarget:target forControlEvent:0]) {
+ SEL selector = NSSelectorFromString(selectorString);
+
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
+ invocation.selector = selector;
+ UIEvent *event = nil;
+ [invocation setArgument:&event atIndex:2];
+
+ [invocation invokeWithTarget:target];
+ }
+ }
+};
+
+itShouldBehaveLike(RACViewChannelExamples, ^{
+ return @{
+ RACViewChannelExampleCreateViewBlock: ^{
+ return [[UISlider alloc] init];
+ },
+ RACViewChannelExampleCreateTerminalBlock: ^(UISlider *view) {
+ return [view rac_newValueChannelWithNilValue:@0.0];
+ },
+ RACViewChannelExampleKeyPath: @keypath(UISlider.new, value),
+ RACViewChannelExampleSetViewValueBlock: setViewValueBlock
+ };
+});
+
+it(@"should send on the returned signal when matching actions are sent", ^{
+ UIControl *control = [RACTestUIButton button];
+ expect(control).notTo.beNil();
+
+ __block NSUInteger receivedCount = 0;
+ [[control
+ rac_signalForControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]
+ subscribeNext:^(UIControl *sender) {
+ expect(sender).to.beIdenticalTo(control);
+ receivedCount++;
+ }];
+
+ expect(receivedCount).to.equal(0);
+
+ [control sendActionsForControlEvents:UIControlEventTouchUpInside];
+ expect(receivedCount).to.equal(1);
+
+ // Should do nothing.
+ [control sendActionsForControlEvents:UIControlEventTouchDown];
+ expect(receivedCount).to.equal(1);
+
+ [control sendActionsForControlEvents:UIControlEventTouchUpOutside];
+ expect(receivedCount).to.equal(2);
+});
+
+it(@"should send completed when the control is deallocated", ^{
+ __block BOOL completed = NO;
+ __block BOOL deallocated = NO;
+
+ @autoreleasepool {
+ UIControl *control __attribute__((objc_precise_lifetime)) = [RACTestUIButton button];
+ [control.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
+ deallocated = YES;
+ }]];
+
+ [[control
+ rac_signalForControlEvents:UIControlEventTouchDown]
+ subscribeCompleted:^{
+ completed = YES;
+ }];
+
+ expect(deallocated).to.beFalsy();
+ expect(completed).to.beFalsy();
+ }
+
+ expect(deallocated).to.beTruthy();
+ expect(completed).to.beTruthy();
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default-568h@2x.png b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default-568h@2x.png
new file mode 100644
index 0000000..0891b7a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default-568h@2x.png
Binary files differ
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default.png b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default.png
new file mode 100644
index 0000000..4c8ca6f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default.png
Binary files differ
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default@2x.png b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default@2x.png
new file mode 100644
index 0000000..35b84cf
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/Default@2x.png
Binary files differ
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.h b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.h
new file mode 100644
index 0000000..88abcec
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.h
@@ -0,0 +1,17 @@
+//
+// RACAppDelegate.h
+// ReactiveCocoa-iOS-UIKitTestHost
+//
+// Created by Andrew Mackenzie-Ross on 27/06/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface RACAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (nonatomic, strong) UIWindow *window;
+
++ (instancetype)delegate;
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.m
new file mode 100644
index 0000000..6af5af8
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACAppDelegate.m
@@ -0,0 +1,24 @@
+//
+// RACAppDelegate.m
+// ReactiveCocoa-iOS-UIKitTestHost
+//
+// Created by Andrew Mackenzie-Ross on 27/06/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACAppDelegate.h"
+
+@implementation RACAppDelegate
+
++ (instancetype)delegate {
+ return (id)UIApplication.sharedApplication.delegate;
+}
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ [self.window makeKeyAndVisible];
+
+ return YES;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.h b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.h
new file mode 100644
index 0000000..a4163aa
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.h
@@ -0,0 +1,13 @@
+//
+// RACTestTableViewController.h
+// ReactiveCocoa
+//
+// Created by Syo Ikeda on 12/30/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface RACTestTableViewController : UITableViewController
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.m
new file mode 100644
index 0000000..0a9d496
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/RACTestTableViewController.m
@@ -0,0 +1,39 @@
+//
+// RACTestTableViewController.m
+// ReactiveCocoa
+//
+// Created by Syo Ikeda on 12/30/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACTestTableViewController.h"
+
+@implementation RACTestTableViewController
+
+- (instancetype)initWithStyle:(UITableViewStyle)style {
+ self = [super initWithStyle:style];
+ if (self == nil) return nil;
+
+ [self.tableView registerClass:UITableViewHeaderFooterView.class forHeaderFooterViewReuseIdentifier:NSStringFromClass(self.class)];
+
+ return self;
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
+ UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:NSStringFromClass(self.class)];
+ return headerView;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(self.class)] ?: [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass(self.class)];
+}
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return 2;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return 20;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist
new file mode 100644
index 0000000..9d6f424
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.reactivecocoa.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch
new file mode 100644
index 0000000..d6ec33c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/ReactiveCocoa-iOS-UIKitTestHost-Prefix.pch
@@ -0,0 +1,14 @@
+//
+// Prefix header for all source files of the 'ReactiveCocoa-iOS-UIKitTestHost' target in the 'ReactiveCocoa-iOS-UIKitTestHost' project
+//
+
+#import <Availability.h>
+
+#ifndef __IPHONE_3_0
+#warning "This project uses features only available in iOS SDK 3.0 and later."
+#endif
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+#endif
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/en.lproj/InfoPlist.strings b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/main.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/main.m
new file mode 100644
index 0000000..7dfdd3f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTestHost/main.m
@@ -0,0 +1,18 @@
+//
+// main.m
+// ReactiveCocoa-iOS-UIKitTestHost
+//
+// Created by Andrew Mackenzie-Ross on 27/06/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "RACAppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([RACAppDelegate class]));
+ }
+}
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTest-Prefix.pch b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTest-Prefix.pch
new file mode 100644
index 0000000..97b985e
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTest-Prefix.pch
@@ -0,0 +1,2 @@
+#import "ReactiveCocoa-iOS-UIKitTest-Prefix.pch"
+#import "ReactiveCocoaTests-Prefix.pch"
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist
new file mode 100644
index 0000000..efa4ce0
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/ReactiveCocoa-iOS-UIKitTestHostTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.reactivecocoa.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UICollectionReusableViewRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UICollectionReusableViewRACSupportSpec.m
new file mode 100644
index 0000000..5ffe160
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UICollectionReusableViewRACSupportSpec.m
@@ -0,0 +1,73 @@
+//
+// UICollectionViewCellRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Kent Wong on 2013-10-04.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACAppDelegate.h"
+
+#import "RACSignal.h"
+#import "RACUnit.h"
+#import "UICollectionReusableView+RACSignalSupport.h"
+
+@interface TestCollectionViewController : UICollectionViewController
+@end
+
+SpecBegin(UICollectionReusableViewRACSupport)
+
+__block UICollectionViewFlowLayout *collectionViewFlowLayout;
+__block TestCollectionViewController *collectionViewController;
+
+beforeEach(^{
+ collectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
+ CGSize screenSize = UIScreen.mainScreen.bounds.size;
+ collectionViewFlowLayout.itemSize = CGSizeMake(screenSize.width, screenSize.height / 2);
+
+ collectionViewController = [[TestCollectionViewController alloc] initWithCollectionViewLayout:collectionViewFlowLayout];
+ expect(collectionViewController).notTo.beNil();
+
+ [collectionViewController.collectionView registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:NSStringFromClass(collectionViewController.class)];
+
+ RACAppDelegate.delegate.window.rootViewController = collectionViewController;
+ expect(collectionViewController.collectionView.visibleCells.count).will.beGreaterThan(0);
+});
+
+it(@"should send on rac_prepareForReuseSignal", ^{
+ UICollectionViewCell *cell = collectionViewController.collectionView.visibleCells[0];
+
+ __block NSUInteger invocationCount = 0;
+ [cell.rac_prepareForReuseSignal subscribeNext:^(id value) {
+ expect(value).to.equal(RACUnit.defaultUnit);
+ ++invocationCount;
+ }];
+
+ expect(invocationCount).to.equal(0);
+
+ // The following two expectations will fail in the iOS 7 simulator, but pass on an iPad 4th gen device running iOS 7.
+ // This appears to be a known issue with the iOS 7 simulator. See http://openradar.appspot.com/14973972
+ // These tests will pass with iOS 6.0 and 6.1.
+ if ([UIDevice.currentDevice.systemVersion compare:@"7.0" options:NSNumericSearch] == NSOrderedAscending ||
+ (!TARGET_IPHONE_SIMULATOR && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)) {
+ [collectionViewController.collectionView reloadData];
+ expect(invocationCount).will.equal(1);
+
+ [collectionViewController.collectionView reloadData];
+ expect(invocationCount).will.equal(2);
+ }
+});
+
+SpecEnd
+
+@implementation TestCollectionViewController
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
+ return [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(self.class) forIndexPath:indexPath];
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
+ return 20;
+}
+
+@end
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewCellRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewCellRACSupportSpec.m
new file mode 100644
index 0000000..2a69143
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewCellRACSupportSpec.m
@@ -0,0 +1,46 @@
+//
+// UITableViewCellRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-07-23.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACAppDelegate.h"
+#import "RACTestTableViewController.h"
+
+#import "RACSignal.h"
+#import "RACUnit.h"
+#import "UITableViewCell+RACSignalSupport.h"
+
+SpecBegin(UITableViewCellRACSupport)
+
+__block RACTestTableViewController *tableViewController;
+
+beforeEach(^{
+ tableViewController = [[RACTestTableViewController alloc] initWithStyle:UITableViewStylePlain];
+ expect(tableViewController).notTo.beNil();
+
+ RACAppDelegate.delegate.window.rootViewController = tableViewController;
+ expect(tableViewController.tableView.visibleCells.count).will.beGreaterThan(0);
+});
+
+it(@"should send on rac_prepareForReuseSignal", ^{
+ UITableViewCell *cell = tableViewController.tableView.visibleCells[0];
+
+ __block NSUInteger invocationCount = 0;
+ [cell.rac_prepareForReuseSignal subscribeNext:^(id value) {
+ expect(value).to.equal(RACUnit.defaultUnit);
+ invocationCount++;
+ }];
+
+ expect(invocationCount).to.equal(0);
+
+ [tableViewController.tableView reloadData];
+ expect(invocationCount).will.equal(1);
+
+ [tableViewController.tableView reloadData];
+ expect(invocationCount).will.equal(2);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewHeaderFooterViewRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewHeaderFooterViewRACSupportSpec.m
new file mode 100644
index 0000000..4658ae9
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITableViewHeaderFooterViewRACSupportSpec.m
@@ -0,0 +1,51 @@
+//
+// UITableViewHeaderFooterViewRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Syo Ikeda on 12/30/13.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACAppDelegate.h"
+#import "RACTestTableViewController.h"
+
+#import "RACSignal.h"
+#import "RACUnit.h"
+#import "UITableViewHeaderFooterView+RACSignalSupport.h"
+
+SpecBegin(UITableViewHeaderFooterViewRACSupportSpec)
+
+__block RACTestTableViewController *tableViewController;
+
+beforeEach(^{
+ tableViewController = [[RACTestTableViewController alloc] initWithStyle:UITableViewStylePlain];
+ expect(tableViewController).notTo.beNil();
+
+ RACAppDelegate.delegate.window.rootViewController = tableViewController;
+ expect([tableViewController.tableView headerViewForSection:0]).notTo.beNil();
+});
+
+it(@"should send on rac_prepareForReuseSignal", ^{
+ UITableViewHeaderFooterView *headerView = [tableViewController.tableView headerViewForSection:0];
+
+ __block NSUInteger invocationCount = 0;
+ [headerView.rac_prepareForReuseSignal subscribeNext:^(id value) {
+ expect(value).to.equal(RACUnit.defaultUnit);
+ invocationCount++;
+ }];
+
+ expect(invocationCount).to.equal(0);
+
+ void (^scrollToSectionForReuse)(NSInteger) = ^(NSInteger section) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
+ [tableViewController.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
+ };
+
+ scrollToSectionForReuse(tableViewController.tableView.numberOfSections - 1);
+ expect(invocationCount).will.equal(1);
+
+ scrollToSectionForReuse(0);
+ expect(invocationCount).will.equal(2);
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextFieldRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextFieldRACSupportSpec.m
new file mode 100644
index 0000000..0aeb49c
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextFieldRACSupportSpec.m
@@ -0,0 +1,53 @@
+//
+// UITextFieldRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "UITextField+RACSignalSupport.h"
+
+SpecBegin(UITextFieldRACSupport)
+
+describe(@"-rac_textSignal", ^{
+ __block UITextField *textField;
+
+ beforeEach(^{
+ textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
+ expect(textField).notTo.beNil();
+ });
+
+ it(@"should start with the initial text", ^{
+ textField.text = @"foo";
+
+ RACSignal *textSignal = textField.rac_textSignal;
+ expect([textSignal first]).to.equal(@"foo");
+
+ textField.text = @"bar";
+ expect([textSignal first]).to.equal(@"bar");
+ });
+
+ it(@"should clear text upon editing", ^{
+ textField.text = @"foo";
+ textField.clearsOnBeginEditing = YES;
+
+ UIWindow *win = [UIWindow new];
+ [win addSubview:textField];
+
+ __block NSString *str = @"bar";
+
+ RACSignal *textSignal = textField.rac_textSignal;
+ [textSignal subscribeNext:^(id x) {
+ str = x;
+ }];
+ expect(str).to.equal(@"foo");
+
+ [textField becomeFirstResponder];
+ expect(str).to.equal(@"");
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextViewRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextViewRACSupportSpec.m
new file mode 100644
index 0000000..f46e17a
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/UITextViewRACSupportSpec.m
@@ -0,0 +1,34 @@
+//
+// UITextViewRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Justin Spahr-Summers on 2013-06-22.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "RACSignal.h"
+#import "RACSignal+Operations.h"
+#import "UITextView+RACSignalSupport.h"
+
+SpecBegin(UITextViewRACSupport)
+
+describe(@"-rac_textSignal", ^{
+ __block UITextView *textView;
+
+ beforeEach(^{
+ textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
+ expect(textView).notTo.beNil();
+ });
+
+ it(@"should start with the initial text", ^{
+ textView.text = @"foo";
+
+ RACSignal *textSignal = textView.rac_textSignal;
+ expect([textSignal first]).to.equal(@"foo");
+
+ textView.text = @"bar";
+ expect([textSignal first]).to.equal(@"bar");
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/en.lproj/InfoPlist.strings b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIKit/UIKitTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/UIRefreshControlRACSupportSpec.m b/ReactiveCocoaFramework/ReactiveCocoaTests/UIRefreshControlRACSupportSpec.m
new file mode 100644
index 0000000..0a9b651
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/UIRefreshControlRACSupportSpec.m
@@ -0,0 +1,68 @@
+//
+// UIRefreshControlRACSupportSpec.m
+// ReactiveCocoa
+//
+// Created by Dave Lee on 2013-10-17.
+// Copyright (c) 2013 GitHub, Inc. All rights reserved.
+//
+
+#import "UIRefreshControl+RACCommandSupport.h"
+#import "NSObject+RACSelectorSignal.h"
+#import "RACControlCommandExamples.h"
+#import "RACCommand.h"
+#import "RACSignal.h"
+
+SpecBegin(UIRefreshControlRACSupport)
+
+describe(@"UIRefreshControl", ^{
+ __block UIRefreshControl *refreshControl;
+
+ beforeEach(^{
+ refreshControl = [[UIRefreshControl alloc] init];
+ expect(refreshControl).notTo.beNil();
+ });
+
+ itShouldBehaveLike(RACControlCommandExamples, ^{
+ return @{
+ RACControlCommandExampleControl: refreshControl,
+ RACControlCommandExampleActivateBlock: ^(UIRefreshControl *refreshControl) {
+ [refreshControl sendActionsForControlEvents:UIControlEventValueChanged];
+ }
+ };
+ });
+
+ describe(@"finishing", ^{
+ __block RACSignal *commandSignal;
+ __block BOOL refreshingEnded;
+
+ beforeEach(^{
+ refreshControl.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
+ return commandSignal;
+ }];
+
+ // Just -rac_signalForSelector: posing as a mock.
+ refreshingEnded = NO;
+ [[refreshControl
+ rac_signalForSelector:@selector(endRefreshing)]
+ subscribeNext:^(id _) {
+ refreshingEnded = YES;
+ }];
+ });
+
+ it(@"should call -endRefreshing upon completion", ^{
+ commandSignal = [RACSignal empty];
+
+ [refreshControl sendActionsForControlEvents:UIControlEventValueChanged];
+ expect(refreshingEnded).will.beTruthy();
+ });
+
+ it(@"should call -endRefreshing upon error", ^{
+ commandSignal = [RACSignal error:[NSError errorWithDomain:@"" code:1 userInfo:nil]];
+
+ [refreshControl sendActionsForControlEvents:UIControlEventValueChanged];
+ expect(refreshingEnded).will.beTruthy();
+ });
+ });
+});
+
+SpecEnd
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/en.lproj/InfoPlist.strings b/ReactiveCocoaFramework/ReactiveCocoaTests/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/ReactiveCocoaFramework/ReactiveCocoaTests/test-data.json b/ReactiveCocoaFramework/ReactiveCocoaTests/test-data.json
new file mode 100644
index 0000000..0599ee5
--- /dev/null
+++ b/ReactiveCocoaFramework/ReactiveCocoaTests/test-data.json
@@ -0,0 +1,5 @@
+[
+ { "item": 1 },
+ { "item": 2 },
+ { "item": 3 }
+]
diff --git a/script/LICENSE.md b/script/LICENSE.md
new file mode 100644
index 0000000..8d92384
--- /dev/null
+++ b/script/LICENSE.md
@@ -0,0 +1,18 @@
+**Copyright (c) 2013 Justin Spahr-Summers**
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/script/README.md b/script/README.md
new file mode 100644
index 0000000..f66206f
--- /dev/null
+++ b/script/README.md
@@ -0,0 +1,82 @@
+# objc-build-scripts
+
+This project is a collection of scripts created with two goals:
+
+ 1. To standardize how Objective-C projects are bootstrapped after cloning
+ 1. To easily build Objective-C projects on continuous integration servers
+
+## Scripts
+
+Right now, there are two important scripts: [`bootstrap`](#bootstrap) and
+[`cibuild`](#cibuild). Both are Bash scripts, to maximize compatibility and
+eliminate pesky system configuration issues (like setting up a working Ruby
+environment).
+
+The structure of the scripts on disk is meant to follow that of a typical Ruby
+project:
+
+```
+script/
+ bootstrap
+ cibuild
+```
+
+### bootstrap
+
+This script is responsible for bootstrapping (initializing) your project after
+it's been checked out. Here, you should install or clone any dependencies that
+are required for a working build and development environment.
+
+By default, the script will verify that [xctool][] is installed, then initialize
+and update submodules recursively. If any submodules contain `script/bootstrap`,
+that will be run as well.
+
+To check that other tools are installed, you can set the `REQUIRED_TOOLS`
+environment variable before running `script/bootstrap`, or edit it within the
+script directly. Note that no installation is performed automatically, though
+this can always be added within your specific project.
+
+### cibuild
+
+This script is responsible for building the project, as you would want it built
+for continuous integration. This is preferable to putting the logic on the CI
+server itself, since it ensures that any changes are versioned along with the
+source.
+
+By default, the script will run [`bootstrap`](#bootstrap), look for any Xcode
+workspace or project in the working directory, then build all targets/schemes
+(as found by `xcodebuild -list`) using [xctool][].
+
+You can also specify the schemes to build by passing them into the script:
+
+```sh
+script/cibuild ReactiveCocoa-Mac ReactiveCocoa-iOS
+```
+
+As with the `bootstrap` script, there are several environment variables that can
+be used to customize behavior. They can be set on the command line before
+invoking the script, or the defaults changed within the script directly.
+
+## Getting Started
+
+To add the scripts to your project, read the contents of this repository into
+a `script` folder:
+
+```
+$ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git
+$ git fetch objc-build-scripts
+$ git read-tree --prefix=script/ -u objc-build-scripts/master
+```
+
+Then commit the changes, to incorporate the scripts into your own repository's
+history. You can also freely tweak the scripts for your specific project's
+needs.
+
+To merge in upstream changes later:
+
+```
+$ git fetch -p objc-build-scripts
+$ git merge --ff --squash -Xsubtree=script objc-build-scripts/master
+```
+
+[xctool]: https://github.com/facebook/xctool
diff --git a/script/bootstrap b/script/bootstrap
new file mode 100755
index 0000000..89cbb8e
--- /dev/null
+++ b/script/bootstrap
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+export SCRIPT_DIR=$(dirname "$0")
+
+##
+## Configuration Variables
+##
+
+config ()
+{
+ # A whitespace-separated list of executables that must be present and locatable.
+ : ${REQUIRED_TOOLS="xctool"}
+
+ export REQUIRED_TOOLS
+}
+
+##
+## Bootstrap Process
+##
+
+main ()
+{
+ config
+
+ if [ -n "$REQUIRED_TOOLS" ]
+ then
+ echo "*** Checking dependencies..."
+ check_deps
+ fi
+
+ local submodules=$(git submodule status)
+ local result=$?
+
+ if [ "$result" -ne "0" ]
+ then
+ exit $result
+ fi
+
+ if [ -n "$submodules" ]
+ then
+ echo "*** Updating submodules..."
+ update_submodules
+ fi
+}
+
+check_deps ()
+{
+ for tool in $REQUIRED_TOOLS
+ do
+ which -s "$tool"
+ if [ "$?" -ne "0" ]
+ then
+ echo "*** Error: $tool not found. Please install it and bootstrap again."
+ exit 1
+ fi
+ done
+}
+
+bootstrap_submodule ()
+{
+ local bootstrap="script/bootstrap"
+
+ if [ -e "$bootstrap" ]
+ then
+ echo "*** Bootstrapping $name..."
+ "$bootstrap" >/dev/null
+ else
+ update_submodules
+ fi
+}
+
+update_submodules ()
+{
+ git submodule sync --quiet && git submodule update --init && git submodule foreach --quiet bootstrap_submodule
+}
+
+export -f bootstrap_submodule
+export -f update_submodules
+
+main
diff --git a/script/cibuild b/script/cibuild
new file mode 100755
index 0000000..a604019
--- /dev/null
+++ b/script/cibuild
@@ -0,0 +1,156 @@
+#!/bin/bash
+
+export SCRIPT_DIR=$(dirname "$0")
+
+##
+## Configuration Variables
+##
+
+SCHEMES="$@"
+
+config ()
+{
+ # The workspace to build.
+ #
+ # If not set and no workspace is found, the -workspace flag will not be passed
+ # to `xctool`.
+ #
+ # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will
+ # take precedence.
+ : ${XCWORKSPACE=}
+
+ # The project to build.
+ #
+ # If not set and no project is found, the -project flag will not be passed
+ # to `xctool`.
+ #
+ # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will
+ # take precedence.
+ : ${XCODEPROJ="ReactiveCocoaFramework/ReactiveCocoa.xcodeproj"}
+
+ # A bootstrap script to run before building.
+ #
+ # If this file does not exist, it is not considered an error.
+ : ${BOOTSTRAP="$SCRIPT_DIR/bootstrap"}
+
+ # Extra options to pass to xctool.
+ : ${XCTOOL_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"}
+
+ # A whitespace-separated list of default schemes to build.
+ #
+ # Individual names can be quoted to avoid word splitting.
+ : ${SCHEMES:=$(xcodebuild -list -project "$XCODEPROJ" 2>/dev/null | awk -f "$SCRIPT_DIR/schemes.awk")}
+
+ export XCWORKSPACE
+ export XCODEPROJ
+ export BOOTSTRAP
+ export XCTOOL_OPTIONS
+ export SCHEMES
+}
+
+##
+## Build Process
+##
+
+main ()
+{
+ config
+
+ if [ -f "$BOOTSTRAP" ]
+ then
+ echo "*** Bootstrapping..."
+ "$BOOTSTRAP" || exit $?
+ fi
+
+ echo "*** The following schemes will be built:"
+ echo "$SCHEMES" | xargs -n 1 echo " "
+ echo
+
+ echo "$SCHEMES" | xargs -n 1 | (
+ local status=0
+
+ while read scheme
+ do
+ build_scheme "$scheme" || status=1
+ done
+
+ exit $status
+ )
+}
+
+find_pattern ()
+{
+ ls -d $1 2>/dev/null | head -n 1
+}
+
+run_xctool ()
+{
+ if [ -n "$XCWORKSPACE" ]
+ then
+ xctool -workspace "$XCWORKSPACE" $XCTOOL_OPTIONS "$@" 2>&1
+ elif [ -n "$XCODEPROJ" ]
+ then
+ xctool -project "$XCODEPROJ" $XCTOOL_OPTIONS "$@" 2>&1
+ else
+ echo "*** No workspace or project file found."
+ exit 1
+ fi
+}
+
+parse_build ()
+{
+ awk -f "$SCRIPT_DIR/xctool.awk" 2>&1 >/dev/null
+}
+
+build_scheme ()
+{
+ local scheme=$1
+
+ echo "*** Cleaning $scheme..."
+ run_xctool -scheme "$scheme" clean >/dev/null || exit $?
+
+ echo "*** Building and testing $scheme..."
+ echo
+
+ local sdkflag=
+ local action=test
+
+ # Determine whether we can run unit tests for this target.
+ run_xctool -scheme "$scheme" run-tests | parse_build
+
+ local awkstatus=$?
+
+ if [ "$awkstatus" -ne "0" ]
+ then
+ # Unit tests aren't supported.
+ action=build
+ fi
+
+ if [ "$awkstatus" -eq "1" ]
+ then
+ # Build for iOS.
+ sdkflag="-sdk iphonesimulator"
+ fi
+
+ run_xctool $sdkflag -scheme "$scheme" $action | awk '{ print; } /Failed to query the list of test cases in the test bundle/ { exit 1; }'
+
+ local awkstatus=$?
+ local result=${PIPESTATUS[0]}
+
+ if [ "$awkstatus" -eq "1" ]
+ then
+ echo
+ echo "*** Mac application tests are currently buggy, so they have been skipped."
+ echo "*** See https://github.com/facebook/xctool/issues/243 for more information."
+ echo
+ return 0
+ fi
+
+ return $result
+}
+
+export -f build_scheme
+export -f run_xctool
+export -f parse_build
+
+main
diff --git a/script/schemes.awk b/script/schemes.awk
new file mode 100644
index 0000000..d101b4f
--- /dev/null
+++ b/script/schemes.awk
@@ -0,0 +1,12 @@
+BEGIN {
+ FS = "\n";
+}
+
+/Targets:/ {
+ while (getline && $0 != "") {
+ if ($0 ~ /Test/) continue;
+
+ sub(/^ +/, "");
+ print "'" $0 "'";
+ }
+}
diff --git a/script/targets.awk b/script/targets.awk
new file mode 100644
index 0000000..117660d
--- /dev/null
+++ b/script/targets.awk
@@ -0,0 +1,12 @@
+BEGIN {
+ FS = "\n";
+}
+
+/Targets:/ {
+ while (getline && $0 != "") {
+ if ($0 ~ /Tests/) continue;
+
+ sub(/^ +/, "");
+ print "'" $0 "'";
+ }
+}
diff --git a/script/xcodebuild.awk b/script/xcodebuild.awk
new file mode 100644
index 0000000..c746b09
--- /dev/null
+++ b/script/xcodebuild.awk
@@ -0,0 +1,35 @@
+# Exit statuses:
+#
+# 0 - No errors found.
+# 1 - Build or test failure. Errors will be logged automatically.
+# 2 - Untestable target. Retry with the "build" action.
+
+BEGIN {
+ status = 0;
+}
+
+{
+ print;
+ fflush(stdout);
+}
+
+/is not valid for Testing/ {
+ exit 2;
+}
+
+/[0-9]+: (error|warning):/ {
+ errors = errors $0 "\n";
+}
+
+/(TEST|BUILD) FAILED/ {
+ status = 1;
+}
+
+END {
+ if (length(errors) > 0) {
+ print "\n*** All errors:\n" errors;
+ }
+
+ fflush(stdout);
+ exit status;
+}
diff --git a/script/xctool.awk b/script/xctool.awk
new file mode 100644
index 0000000..f613258
--- /dev/null
+++ b/script/xctool.awk
@@ -0,0 +1,25 @@
+# Exit statuses:
+#
+# 0 - No errors found.
+# 1 - Wrong SDK. Retry with SDK `iphonesimulator`.
+# 2 - Missing target.
+
+BEGIN {
+ status = 0;
+}
+
+{
+ print;
+}
+
+/Testing with the '(.+)' SDK is not yet supported/ {
+ status = 1;
+}
+
+/does not contain a target named/ {
+ status = 2;
+}
+
+END {
+ exit status;
+}