Permalink
Browse files

Move "When to use RAC" into the README

  • Loading branch information...
jspahrsummers committed Feb 18, 2013
1 parent 6499987 commit 567d449f7af3e540441620db6d118a875df1a048
Showing with 232 additions and 233 deletions.
  1. +1 −233 Documentation/DesignGuidelines.md
  2. +231 −0 README.md
@@ -4,17 +4,10 @@ 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).
-Most of this document (except for the first section) assumes basic familiarity
+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.
-**[When to use RAC](#when-to-use-rac)**
-
- 1. [Handling asynchronous or event-driven data sources](#handling-asynchronous-or-event-driven-data-sources)
- 1. [Chaining dependent operations](#chaining-dependent-operations)
- 1. [Parallelizing independent work](#parallelizing-independent-work)
- 1. [Simplifying collection transformations](#simplifying-collection-transformations)
-
**[The RACSequence contract](#the-racsequence-contract)**
1. [Evaluation occurs lazily by default](#evaluation-occurs-lazily-by-default)
@@ -52,231 +45,6 @@ resource for getting up to speed on the functionality provided by RAC.
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)
-## When to use RAC
-
-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
-- (void)viewDidLoad {
- [super viewDidLoad];
-
- [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)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 ([object isEqual:[LoginManager sharedManager]] && [keyPath isEqualToString:@"loggingIn"]) {
- [self updateLogInButton];
- }
-}
-```
-
-… 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,
- RACAbleWithStart(LoginManager.sharedManager, loggingIn),
- RACAbleWithStart(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;
- }];
- }];
-}
-```
-
-### 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]
- sequenceNext:^{
- 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;
-
-dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- @synchronized (self) {
- databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate];
- if (fileContents != nil) {
- [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
- NSLog(@"Done processing");
- }
- }
-});
-
-dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSMutableArray *filesInProgress = [NSMutableArray array];
- for (NSString *path in files) {
- [filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
- }
-
- @synchronized (self) {
- fileContents = [filesInProgress copy];
- if (databaseObjects != nil) {
- [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
- NSLog(@"Done processing");
- }
- }
-});
-```
-
-The above code can be cleaned up and optimized by simply composing signals:
-
-```objc
-RACSignal *databaseSignal = [[databaseClient
- fetchObjectsMatchingPredicate:predicate]
- subscribeOn:[RACScheduler schedulerWithPriority:RACSchedulerPriorityDefault]];
-
-RACSignal *fileSignal = [RACSignal start:^(BOOL *success, NSError **error) {
- NSMutableArray *filesInProgress = [NSMutableArray array];
- for (NSString *path in files) {
- [filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
- }
-
- return [filesInProgress copy];
-}];
-
-[[RACSignal
- combineLatest:@[ databaseSignal, fileSignal ]
- reduce:^(NSArray *databaseObjects, NSArray *fileContents) {
- [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
- }]
- 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"];
- }];
-```
-
## The RACSequence contract
[RACSequence][] is a _pull-driven_ stream. Sequences behave similarly to
Oops, something went wrong.

0 comments on commit 567d449

Please sign in to comment.