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.
RACAble
and RACAbleWithStart
have been replaced with a single RACObserve macro. RACObserve
always starts with the current value of the property, and will notice the deallocation 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:
RACAbleWithStart(self.key)
with RACObserve(self, key)
.RACAble(self.key)
with [RACObserve(self, key) skip:1]
(if skipping the starting value is necessary).RACCommand
has been completely refactored. 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.
All together, these improvements should make RACCommand
more composable and less imperative, so it fits into the framework better.
To update:
-addSignalBlock:
or -subscribeNext:
into the signalBlock
passed to the initializer.-addSignalBlock:
, subscribe to executionSignals
or the result of -execute:
instead.RACAbleWithStart(command, executing)
with command.executing
.RACAbleWithStart(command, canExecute)
with command.enabled
.deliverOn:RACScheduler.mainThreadScheduler
on RACCommand
properties, as they are now unnecessary.The RAC
macro now always requires two or three arguments:
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:
RAC(self.objectProperty)
with RAC(self, objectProperty)
.RAC(self, integerProperty, @5)
-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, so that it's clear how events should be delivered.
To update:
[RACScheduler scheduler]
. Note that this creates a new background scheduler for events to arrive upon.-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.-rac_signalForSelector:
has been completely refactored 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:
-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.+rac_signalForSelector:
by implementing the class method and sending arguments onto a RACSubject
instead.RACPropertySubject
and RACBinding
have been replaced 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:
RACPropertySubject
, create a RACChannel
. Replace N-way property subjects (where N is greater than 2) with multiple RACChannel
s.RACObservablePropertySubject
, create a RACKVOChannel
or use the RACChannelTo
macro.RACBinding
with RACChannelTerminal
.RACBind(self.objectProperty)
with RACChannelTo(self, objectProperty)
. Add a default value for primitive properties: RACChannelTo(self, integerProperty, @5)
-bindTo:
with the explicit subscription of two endpoints:[binding1.followingEndpoint subscribe:binding2.leadingEndpoint]; [[binding2.leadingEndpoint skip:1] subscribe:binding1.followingEndpoint];
-rac_bind:toObject:withKeyPath:
and related methods have been replaced with -rac_channelToBinding:options:
, which returns a RACChannelTerminal
that can be used as a two-way binding or a one-way signal.
To update:
RACChannel
interface. This bridges Cocoa Bindings with the full power of ReactiveCocoa.-bind:toObject:withKeyPath:options:
with the following options:@{ NSContinuouslyUpdatesValueBindingOption: @YES }
for -rac_bind:toObject:withKeyPath:
@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSNullPlaceholderBindingOption: nilValue }
for -rac_bind:toObject:withKeyPath:nilValue:
@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSValueTransformerBindingOption: valueTransformer }
for -rac_bind:toObject:withKeyPath:transform:
@{ NSContinuouslyUpdatesValueBindingOption: @YES, NSValueTransformerBindingOption: NSNegateBooleanTransformerName }
for -rac_bind:toObject:withNegatedKeyPath:
To make the sequencing and transformation operators less confusing, -sequenceMany:
has been removed, and -sequenceNext:
has been renamed to -then:
.
To update:
-sequenceMany:
with -flattenMap:
and a block that doesn't use its argument.-sequenceNext:
with -then:
.-toProperty:onObject:
and -[NSObject rac_deriveProperty:from:]
have been combined into a new -[RACSignal setKeyPath:onObject:nilValue:]
method.
The nilValue
parameter was added in parallel with the RAC macro, but the semantics are otherwise identical.
To update:
-toProperty:onObject:
and -rac_deriveProperty:from:
with -setKeyPath:onObject:
.[signal setKeyPath:@"integerProperty" onObject:self nilValue:@5]
In the interests of parametricity, -rac_liftSelector:withObjects:
has been replaced 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 in the same way.
To update:
+[RACSignal return:]
and add a nil terminator.+combineLatest:reduce:
.-rac_lift
with -rac_liftSelector:withSignals:
.+start:
, +startWithScheduler:block
, and +startWithScheduler:subjectBlock:
have been combined into a single +startEagerlyWithScheduler:block: 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:
[RACScheduler scheduler]
to match the previous implicit scheduling behavior of +start:
.success
/error
, to send events to the given <RACSubscriber>
instead.-rac_didDeallocSignal
has been removed in favor of -rac_willDeallocSignal, because most teardown should happen before the object becomes invalid.
-rac_addDeallocDisposable:
has also been removed in favor of using the object's rac_deallocDisposable
directly.
To update:
-rac_didDeallocSignal
with rac_willDeallocSignal
.-rac_addDeallocDisposable:
by invoking -addDisposable:
on the object's rac_deallocDisposable
instead.RACQueueScheduler
has been exposed as a public class, so consumers can create their own scheduler implementations using GCD queues.
The RACTargetQueueScheduler subclass replaces the +schedulerWithQueue:name:
method.
To update:
Replace uses of +schedulerWithQueue:name:
with -[RACTargetQueueScheduler initWithName:targetQueue:]
.
NSDate
now replaces 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.
To update:
Replace dispatch_time_t
calculations with NSDate
.
-windowWithStart:close:
and -buffer:
have been removed because they're not well-tested, and their functionality can be achieved with other operators.
-bufferWithTime:
is still supported.
To update:
-windowWithStart:close:
with different patterns.-buffer:
with take, collect, and repeat.NSTask+RACSupport
has been removed, because it was buggy and unsupported.
To update:
Use a vanilla NSTask
, and send events onto RACSubject
s instead.
The RACSubscriber
class (not to be confused with the protocol) should never be used directly, so it has been hidden.
To update:
Replace uses of RACSubscriber
with id<RACSubscriber>
or RACSubject
.
UIButton
now has a rac_command property.
Any command set will be executed when the button is tapped, and the button will be disabled whenever the command is unable to execute.
UIActionSheet
now has a rac_buttonClickedSignal property, which will fire whenever one of the sheet's buttons is clicked.
Documentation has been added 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.
If libextobjc was used in a project that statically linked ReactiveCocoa, duplicate symbol errors could result.
To avoid this issue, RAC now renames the libextobjc symbols that it uses.
RACChannel interfaces have been added 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:
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.
RACSignal
now has the -initially: operator, which executes a given block each time the signal is subscribed to. This is symmetric to -finally:
.
RACTestScheduler
is a new kind 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.