reactive cocoa lightning talk
DESCRIPTION
Talking at iOS Camp Denver (http://iosdevcampcolorado.com) about Reactive Cocoa (https://github.com/ReactiveCocoa/ReactiveCocoa) Video here @3:45: http://www.ustream.tv/channel/iosdevcamp-colorado?utm_campaign=t.co&utm_source=ustre-am&utm_medium=socialTRANSCRIPT
ReactiveCocoaBob Spryn LoHi Labs @sprynmr (github, twitter) !!!https://github.com/ReactiveCocoa/ReactiveCocoa
ReactiveCocoa• An implementation of functional reactive programming
• It provides APIs for composing and transforming streams of values
• “Signals” send you these values over time for you to react to
http://en.wikipedia.org/wiki/Functional_reactive_programming
?
Program Smarter
Not just another API.
It enables you to
Focus on WHAT not HOW
Reactive!!! Declarative!!! What does that even mean?
Reactive!!! Declarative!!! What does that even mean?
• Setup your reactions
• Compose, split, filter, etc.
ImperativeThe way we normally program.
ImperativeThe way we normally program.
• Observe, wait, then command & control
• Dealing with the HOW, not just the WHAT
• Code for controlling flow is spread all over
Auto-layout : Managing Frames ReactiveCocoa : Imperative programming
Complexity Is
STATE
BOOL loadingNextPageOfComments;
BOOL commentsAreShowing;
BOOL commentsLoadingShowing;
BOOL flippingAnswers;
BOOL isOwnerOfQuestion;
2 states
4 states
8 states
16 states
32 states
- (void) updateChangeAnswerButtonText { if (self.didAnswerQuestion && self.inResultsView) { [self.commentHeaderCell configureWithStyle:TCQuestionDetailCommentHeaderCellChangeAnswer]; } else if (self.isOwnerOfQuestion && !self.didAnswerQuestion && self.inResultsView) { [self.commentHeaderCell configureWithStyle:TCQuestionDetailCommentHeaderCellViewAnswers]; } else { [self.commentHeaderCell configureWithStyle:TCQuestionDetailCommentHeaderCellViewStats]; } } - (void) updateViewCommentStatus { if ((self.didAnswerQuestion || self.isOwnerOfQuestion) && !self.commentsAreShowing) { self.commentsAreShowing = YES; self.commentsLoadingShowing = YES; … } [self showCommentBox:self.inResultsView || self.isOwnerOfQuestion]; } !^aBlock { sself.didAnswerQuestion = sself.question.userResponse ? YES : NO; sself.inResultsView = sself.didAnswerQuestion || self.isOwnerOfQuestion;
if (sself.questionFullyLoaded) { return; } …
}
Real code from one file
- (void) prepareForReuse { self.state = TCQuestionDetailViewControllerStateInactive; self.questionFullyLoaded = NO; self.isOwnerOfQuestion = NO; self.didAnswerQuestion = NO; [self showCommentBox:NO]; self.loadingNextPageOfComments = NO; self.commentsAreShowing = NO; self.commentsLoadingShowing = NO; }
ReactiveCocoa to the Rescue!
• Signals instead of mutable variables
• RACSignal – Think of it as a beacon sending out new values to it’s subscribers
RACSignal
• A unified interface for observing and reacting to all kinds of events and their values• UI Action• Async Network Call• KVO• Async Processing
It’s all input!
RACSignal• Shared vocabulary for transforming the received values
• Combine, filter, reduce, map, and many more operations
• Eventually generate output (side effects)
• Updating a label
• Update an array with objects from the network
• etc.
- (void)viewDidLoad { [super viewDidLoad];
[self.username addTarget:self action:@selector(textFieldTextDidChange:) forControlEvents:UIControlEventAllEditingEvents];
[self.email addTarget:self action:@selector(textFieldTextDidChange:) forControlEvents:UIControlEventAllEditingEvents];} !- (void)textFieldTextDidChange:(UITextField *)field { BOOL validUsername = self.username.text.length > 0; NSRange at = [self.email.text rangeOfString:@"@"]; BOOL validEmail = at.location != NSNotFound; self.signupButton.enabled = validUsername && validEmail;}
Your name
Your email address
Sign Up
Solved the imperative way.
signUpButton.enabled
Old
RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ self.name.rac_textSignal, self.email.rac_textSignal ] reduce:^(NSString *name, NSString *email) { NSRange at = [email rangeOfString:@"@"]; return @(at.location != NSNotFound && name.length > 0); }];
Your name
Your email address
Sign Up
Solved the reactive way.
Should I be enabled?
AWESOME
RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ self.name.rac_textSignal, self.email.rac_textSignal ] reduce:^(NSString *name, NSString *email) { NSRange at = [email rangeOfString:@"@"]; return @(at.location != NSNotFound && name.length > 0); }];
Your name
Your email address
Sign Up
Solved the reactive way.
Should I be enabled?
AWESOME
RACSignal *validateEmailSignal = [self.email.rac_textSignal map:^id(NSString *emailString) { return [MyAPI validatedEmail:emailString]; }]; !RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ self.name.rac_textSignal, validateEmailSignal ] reduce:^(NSString *name, APIResponse *response) { return @(response.json.isValid == YES && name.length > 0); }];
Solved the reactive way.AWESOME
Your name
Your email address
Sign Up
Should I be enabled?
Network Validation? Easy.
RACSignal *validateEmailSignal = map }];!RAC combineLatest ] reduce }];
Solved the AWESOME
Your name
Your email address
Sign Up
Should I be enabled?
Network Validation? Easy.
The point is to Minimize State
RACSignal *validateEmailSignal = map }];!RAC combineLatest ] reduce }];
Solved the AWESOME
Your name
Your email address
Sign Up
Should I be enabled?
Network Validation? Easy.
The point is to Minimize State
Check out ReactiveCocoa Your Brain Will Thank You
https://github.com/ReactiveCocoa/ReactiveCocoa
Bob Spryn LoHi Labs @sprynmr (github, twitter) !Thanks to @jspahrsummers, @joshaber, @AshFurrow, @robb, @andrewsardone and @erikprice (github usernames) for sharing their presentation slides and helping me learn ReactiveCocoa