Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
robb
[email protected]
ceterum_censeo
Slide 2
Slide 2 text
Let's talk about ReactiveCocoa
Slide 3
Slide 3 text
Let's talk about ReactiveCocoa state
Slide 4
Slide 4 text
evil √
Slide 5
Slide 5 text
Have you tried turning it off and on again?
Slide 6
Slide 6 text
Have you tried turning it off and on again? Have you tried turning it off and on again?
Slide 7
Slide 7 text
Start Okay Fail
Slide 8
Slide 8 text
Start Okay Fail power-cycle power-cycle
Slide 9
Slide 9 text
state
Slide 10
Slide 10 text
state
Slide 11
Slide 11 text
duh! " " you say
Slide 12
Slide 12 text
e.g.
Slide 13
Slide 13 text
@property (readwrite) BOOL walks; @property (readwrite) BOOL quaks; ! @property (readonly) BOOL duck;
Slide 14
Slide 14 text
duck = walks ∧ quaks
Slide 15
Slide 15 text
! ! - (void)setQuacks:(BOOL)quacks { _quacks = quacks; self.duck = quacks && self.walks; } - ( _walks = walks; self.duck = walks }
Slide 16
Slide 16 text
- (BOOL)duck { return self.walks && self.quacks; } // duck = walks ∧ quaks
Slide 17
Slide 17 text
ReactiveCocoa
Slide 18
Slide 18 text
Functional Reactive Programming
Slide 19
Slide 19 text
Signals instead of variables
Slide 20
Slide 20 text
Pipes, not boxes
Slide 21
Slide 21 text
wtf? " " you may be thinking
Slide 22
Slide 22 text
e.g.
Slide 23
Slide 23 text
RAC(self, duck) = [RACSignal combineLatest:@[ RACObserve(self, walks), RACObserve(self, quacks) ]] reduce:^(NSNumber *walks, NSNumber *quacks) { return @(walks.boolValue && quacks.boolValue); }];
Slide 24
Slide 24 text
RAC(self, duck) = [RACSignal combineLatest:@[ RACObserve(self, walks), RACObserve(self, quacks) ]] reduce:^(NSNumber *walks, NSNumber *quacks) { return @(walks.boolValue && quacks.boolValue); }];
Slide 25
Slide 25 text
RAC(self, duck) = [RACSignal combineLatest:@[ RACObserve(self, walks), RACObserve(self, quacks) ]] reduce:^(NSNumber *walks, NSNumber *quacks) { return @(walks.boolValue && quacks.boolValue); }];
Slide 26
Slide 26 text
RAC(self, duck) = [RACSignal combineLatest:@[ RACObserve(self, walks), RACObserve(self, quacks) ]] reduce:^(NSNumber *walks, NSNumber *quacks) { return @(walks.boolValue && quacks.boolValue); }];
Slide 27
Slide 27 text
& walks talks duck
Slide 28
Slide 28 text
& walks talks duck
Slide 29
Slide 29 text
& walks talks duck
Slide 30
Slide 30 text
& walks talks duck
Slide 31
Slide 31 text
& walks talks duck
Slide 32
Slide 32 text
RACSignal
Slide 33
Slide 33 text
-(RACDisposable *)subscribe:(id)obj; RACSignal
Slide 34
Slide 34 text
Slide 35
Slide 35 text
- (void)sendNext:(id)value;
Slide 36
Slide 36 text
- (void)sendNext:(id)value; - (void)sendCompleted;
Slide 37
Slide 37 text
- (void)sendNext:(id)value; - (void)sendCompleted; - (void)sendError:(NSError *)error;
Slide 38
Slide 38 text
so what?
Slide 39
Slide 39 text
Powerful toolset
Slide 40
Slide 40 text
map
Slide 41
Slide 41 text
filter
Slide 42
Slide 42 text
fold
Slide 43
Slide 43 text
write declarative code
Slide 44
Slide 44 text
e.g.
Slide 45
Slide 45 text
Newsletter Your name Your email address Sign Up Brooklyn 4:00 PM 100%
Slide 46
Slide 46 text
Newsletter Your email address Sign Up robb Brooklyn 4:00 PM 100%
Slide 47
Slide 47 text
Newsletter Sign Up robb robb Brooklyn 4:00 PM 100%
Slide 48
Slide 48 text
Newsletter Sign Up robb @robb.is Sign Up robb Brooklyn 4:00 PM 100%
Slide 49
Slide 49 text
Your name Your email address Sign Up validate Newsletter
Slide 50
Slide 50 text
Your name Your email address Sign Up validate that looks familiar! Newsletter
Slide 51
Slide 51 text
- (void)viewDidLoad { [super viewDidLoad]; [self.username addTarget:self action:@selector(textFieldTextDidChange:) forControlEvents:UIControlEventAllEditingEvents]; [self.email addTarget:self action:@selector(textFieldTextDidChange:) forControlEvents:UIControlEventAllEditingEvents]; } help!
Slide 52
Slide 52 text
- (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; }
Slide 53
Slide 53 text
- (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; }
Slide 54
Slide 54 text
ReactiveCocoa
Slide 55
Slide 55 text
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); }]:
Slide 56
Slide 56 text
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); }]:
Slide 57
Slide 57 text
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); }]:
Slide 58
Slide 58 text
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); }]:
Slide 59
Slide 59 text
Your name Your email address Sign Up validate Newsletter
Slide 60
Slide 60 text
Newsletter Your name Your email address Sign Up Brooklyn 4:00 PM 100%
Slide 61
Slide 61 text
Newsletter Your email address Sign Up robb Brooklyn 4:00 PM 100%
Slide 62
Slide 62 text
Newsletter Sign Up robb Sign Up
[email protected]
Brooklyn 4:00 PM 100%
Slide 63
Slide 63 text
Let's talk about Asynchrony
Slide 64
Slide 64 text
[client logInWithSuccess:^{ [client loadMeUserWithSuccess:^(SPUser *me) { [client loadNewslettersForUser:me withSuccess:^(NSArray *newsletters) { NSLog(@"Help me, " "I'm trapped in callback hell!"); } error:^(NSError *error) { // Handle failure } } error:^(NSError *error) { // Handle failure }]; } error:^(NSError *error) { // Handle failure }];
Slide 65
Slide 65 text
[client logInWithSuccess:^{ [client loadMeUserWithSuccess:^(SPUser *me) { [client loadNewslettersForUser:me withSuccess:^(NSArray *newsletters) { NSLog(@"Help me, " "I'm trapped in callback hell!"); } error:^(NSError *error) { // Handle failure } } error:^(NSError *error) { // Handle failure }]; } error:^(NSError *error) { // Handle failure }]; omg, why?!
Slide 66
Slide 66 text
Let's fix this
Slide 67
Slide 67 text
1. HTTP Client
Slide 68
Slide 68 text
@interface SPHTTPClient : AFHTTPClient - (RACSignal *)enqueueRequest:(NSURLRequest *)req; @end
Slide 69
Slide 69 text
[self enqueueHTTPRequestOperation:operation]; ! - (RACSignal *)enqueueRequest:(NSURLRequest *)req { return [RACSignal createSignal:^(id subscriber) { AFHTTPRequestOperation *operation; operation = [self HTTPRequestOperationWithRequest:req success:^(id operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; }} failure:^(id operation, NSError *error) { [subscriber sendError:error]; }];
Slide 70
Slide 70 text
- (RACSignal *)enqueueRequest:(NSURLRequest *)req { return [RACSignal createSignal:^(id subscriber) { AFHTTPRequestOperation *operation; operation = [self HTTPRequestOperationWithRequest:req success:^(id operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; }} failure:^(id operation, NSError *error) { [subscriber sendError:error]; }]; [self enqueueHTTPRequestOperation:operation]; !
Slide 71
Slide 71 text
[self enqueueHTTPRequestOperation:operation]; ! - (RACSignal *)enqueueRequest:(NSURLRequest *)req { return [RACSignal createSignal:^(id subscriber) { AFHTTPRequestOperation *operation; operation = [self HTTPRequestOperationWithRequest:req success:^(id operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; }} failure:^(id operation, NSError *error) { [subscriber sendError:error]; }];
Slide 72
Slide 72 text
- (RACSignal *)enqueueRequest:(NSURLRequest *)req { return [RACSignal createSignal:^(id subscriber) { AFHTTPRequestOperation *operation; operation = [self HTTPRequestOperationWithRequest:req success:^(id operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; }} failure:^(id operation, NSError *error) { [subscriber sendError:error]; }]; [self enqueueHTTPRequestOperation:operation]; !
Slide 73
Slide 73 text
- (RACSignal *)enqueueRequest:(NSURLRequest *)req { return [RACSignal createSignal:^(id subscriber) { AFHTTPRequestOperation *operation; operation = [self HTTPRequestOperationWithRequest:req success:^(id operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; }} failure:^(id operation, NSError *error) { [subscriber sendError:error]; }]; [self enqueueHTTPRequestOperation:operation]; !
Slide 74
Slide 74 text
[self enqueueHTTPRequestOperation:operation]; ! return [RACDisposable disposableWithBlock:^{ [operation cancel]; }]; }]; success:^( [subscriber sendNext:response]; [subscriber sendCompleted]; } failure:^( [subscriber sendError:error]; }];
Slide 75
Slide 75 text
[self enqueueHTTPRequestOperation:operation]; ! return [RACDisposable disposableWithBlock:^{ [operation cancel]; }]; }]; success:^( [subscriber sendNext:response]; [subscriber sendCompleted]; } failure:^( [subscriber sendError:error]; }];
Slide 76
Slide 76 text
2. API interaction
Slide 77
Slide 77 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 78
Slide 78 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 79
Slide 79 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 80
Slide 80 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 81
Slide 81 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }} check out Mantle!
Slide 82
Slide 82 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 83
Slide 83 text
- (RACSignal *)fetchMeUser return [[self.client getPath:@"/me"] flattenMap:^(id JSON) { NSError *error; SPUser *user = [SPUser userWithJSON:JSON error:&error]; if (user == nil) { return [RACSignal error:error]; } else { return [RACSignal return:user]; }} }]; }}
Slide 84
Slide 84 text
3. Putting it together
Slide 85
Slide 85 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 86
Slide 86 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 87
Slide 87 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 88
Slide 88 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 89
Slide 89 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 90
Slide 90 text
[[[[client logIn] then:^{ return [client fetchMeUser]; }] flattenMap:^(SPUser *me) { return [client fetchNewslettersForUser:me]; }] subscribeNext:^(NSArray *files) { NSLog(@"Files: %@", files); }} error:^(NSError *error) { // Handle error }];
Slide 91
Slide 91 text
UI&IO
Slide 92
Slide 92 text
UI+IO
Slide 93
Slide 93 text
Newsletter Your name Your email address Sign Up Sign Up Brooklyn 4:00 PM 100%
Slide 94
Slide 94 text
Your name Your email address Sign Up validate Newsletter
Slide 95
Slide 95 text
Your name Your email address Sign Up validate Newsletter
Slide 96
Slide 96 text
oh boy
Slide 97
Slide 97 text
oh boy, why me?
Slide 98
Slide 98 text
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); }];
Slide 99
Slide 99 text
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); }];
Slide 100
Slide 100 text
RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ ]] validName, validEmail reduce:^(NSString *name, NSString *email) { NSRange at = [email rangeOfString:@"@"]; return @(at.location != NSNotFound && name.length > 0); }];
Slide 101
Slide 101 text
RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ ]] validName, validEmail reduce:^(NSString *name, NSString *email) { NSRange at = [email rangeOfString:@"@"]; return @(at.location != NSNotFound && name.length > 0); }];
Slide 102
Slide 102 text
RAC(self.signUpButton, enabled) = [RACSignal combineLatest:@[ ]] validName, validEmail reduce:^(NSNumber *name, NSNumber *email) { return @(name.boolValue && email.boolValue); }]; }];
Slide 103
Slide 103 text
RAC(self.signUpButton, enabled) = [RACSignal combineLatest: validName, validEmail reduce:^(NSNumber *name, NSNumber *email) { RACSignal *validName = [self.name.rac_textSignal map:^(NSString *name) { return @(name.length > 0); }]; switchToLatest];
Slide 104
Slide 104 text
RACSignal *validName = [self.name.rac_textSignal map:^(NSString *name) { return }] RACSignal *validEmail = [[self.email.rac_textSignal map:^(NSString *email) { return [[client validateEmail:email] startWith:@NO]; }] switchToLatest];
Slide 105
Slide 105 text
RACSignal *validName = [self.name.rac_textSignal map:^(NSString *name) { return }] RACSignal *validEmail = [[self.email.rac_textSignal map:^(NSString *email) { return [[client validateEmail:email] startWith:@NO]; }] switchToLatest];
Slide 106
Slide 106 text
RACSignal *validName = [self.name.rac_textSignal map:^(NSString *name) { return }] RACSignal *validEmail = [[self.email.rac_textSignal map:^(NSString *email) { return [[client validateEmail:email] startWith:@NO]; }] switchToLatest];
Slide 107
Slide 107 text
Your name Your email address Sign Up validate Newsletter
Slide 108
Slide 108 text
Newsletter Your name Your email address Sign Up Brooklyn 4:00 PM 100%
Slide 109
Slide 109 text
Newsletter Your email address Sign Up robb Brooklyn 4:00 PM 100%
Slide 110
Slide 110 text
Newsletter Sign Up robb
[email protected]
Brooklyn 4:00 PM 100%
Slide 111
Slide 111 text
Newsletter Sign Up robb Sign Up
[email protected]
Brooklyn 4:00 PM 100%
Slide 112
Slide 112 text
Recap
Slide 113
Slide 113 text
+ Declarative code Recap
Slide 114
Slide 114 text
+ Declarative code + Easy handling of asynchronous tasks Recap
Slide 115
Slide 115 text
+ Declarative code + Easy handling of asynchronous tasks + Great Composition Recap
Slide 116
Slide 116 text
+ Declarative code + Easy handling of asynchronous tasks + Great Composition − Conceptual overhead Recap
Slide 117
Slide 117 text
+ Declarative code + Easy handling of asynchronous tasks + Great Composition − Conceptual overhead − Debugging can get tricky Recap
Slide 118
Slide 118 text
+ Declarative code + Easy handling of asynchronous tasks + Great Composition − Conceptual overhead − Debugging can get tricky Recap (dtrace support)
Slide 119
Slide 119 text
Qs?
Slide 120
Slide 120 text
Thanks!
Slide 121
Slide 121 text
iPhone 5 Sketch Template courtesy of Denis Rojčyk ! Cloud icon curtesy of Dmitry Baranovskiy, from The Noun Project