Slide 1

Slide 1 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Functional Reactive Programming on iOS

Slide 2

Slide 2 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup

Slide 3

Slide 3 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup No entry Default Loading 3 High level states

Slide 4

Slide 4 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Button tapped Network error callback Textfield text changed disable disable enable enable …; if text enable else disable textfield textfield button button button button

Slide 5

Slide 5 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup State propagation is handled manually by mutating variables

Slide 6

Slide 6 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup • State handling code is dispersed • Code is hard to read • Code is hard to maintain Problem

Slide 7

Slide 7 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup No entry Default Loading 3 High level states 9 possible invalid states!

Slide 8

Slide 8 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Manual state management is error prone

Slide 9

Slide 9 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup What is functional reactive programming?

Slide 10

Slide 10 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Imperative vs. Declarative

Slide 11

Slide 11 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup A B C 20 10 ? 1. Perform the following steps whenever A or B changes 2. Add 50 to value of A 3. Subtract 10 from value of B 4. Add the results from 2.) and 3.) 5. Write result from 4.) into C Imperative

Slide 12

Slide 12 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Declarative A B C 20 10 ? C = (A+50) + (B-10)

Slide 13

Slide 13 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Imperative

Slide 14

Slide 14 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Button tapped Network error callback Textfield text changed disable disable enable enable …; if text enable else disable textfield textfield textfield textfield button button

Slide 15

Slide 15 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Declarative

Slide 16

Slide 16 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Stateless Function Event emitters Functional Reactive Textfield text changed disable textfield button enable textfield disable enable button decide decide Button tapped Network error callback Button tapped Network error callback

Slide 17

Slide 17 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup State is derived from a defined set of inputs

Slide 18

Slide 18 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Intro to Reactive Cocoa 2.x

Slide 19

Slide 19 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Signals send values over time

Slide 20

Slide 20 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup • Callbacks • Delegate methods • KVO / Property overriding Signals } Values over time

Slide 21

Slide 21 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup RACSignal sendNext: send Completed: send Error: Val 1 Val 2 Subscriber Subscriber Subscriber

Slide 22

Slide 22 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup We can bind Signals OR subscribe to Signals

Slide 23

Slide 23 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Textfield text changed user.email “h” “he” “hel” Bind RAC(self.user, username) = self.usernameTextfield.rac_textSignal

Slide 24

Slide 24 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Subscribe [self.usernameTextfield.rac_textSignal subscribeNext:^(NSString *t) { NSLog(@“New value: %@", x); }]; Textfield text changed “h” “he” “hel” side effects; imperative code;

Slide 25

Slide 25 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Prefer binding over explicit subscription

Slide 26

Slide 26 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model -> View Binding with Reactive Cocoa

Slide 27

Slide 27 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup - (void)awakeFromNib { RAC(self, avatarImageView.image) = RACObserve(self, model.avatar); RAC(self, nameLabel.text) = RACObserve(self, model.name); // more binding code } View updates whenever
 model or model properties change

Slide 28

Slide 28 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model View ✓

Slide 29

Slide 29 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model View ?

Slide 30

Slide 30 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Textfield text changed button. enabled map “nsmeetup” YES Textfield text changed button. enabled map “” NO Signal Operators

Slide 31

Slide 31 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup RACCommand Execution Signals Subscriber Signal 1 Signal 2 Subscriber Val 1 Val 1

Slide 32

Slide 32 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model View ?

Slide 33

Slide 33 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model ViewModel View Stores View state, communicates
 with model Stores model state, provides business logic Bindings

Slide 34

Slide 34 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup

Slide 35

Slide 35 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup PersonAddingView* PersonAddingViewModel usernameSearchText addButtonCommand
 addButtonEnabledSignal usernameTextfield.text addButton.rac_command *some variables have been renamed for brevity

Slide 36

Slide 36 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup self.addTwitterButton.rac_command = self.viewModel.addTwitterButtonCommand; RAC(self.usernameTextfield, enabled) = self.viewModel.textFieldEnabledSignal; PersonAddingView Initialization

Slide 37

Slide 37 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup self.addButtonEnabledSignal = [RACObserve(self, usernameSearchText) map:^id(NSString *searchText) { if (!searchText || [searchText isEqualToString:@""]) { return @(NO); } else { return @(YES); } }]; PersonAddingViewModel Enabling / Disabling the add button

Slide 38

Slide 38 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup PersonAddingViewModel @"" NO

Slide 39

Slide 39 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup PersonAddingViewModel @"nsmeetup" YES

Slide 40

Slide 40 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Networking with Reactive Cocoa

Slide 41

Slide 41 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup

Slide 42

Slide 42 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup PersonAddingView PersonDetailView PersonContainerView {

Slide 43

Slide 43 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup self.addTwitterButtonCommand = [[RACCommand alloc] initWithEnabled:self.addButtonEnabledSignal signalBlock:^RACSignal *(id input) { RACSignal *signal = [self.twitterClient infoForUsername:self.usernameSearchText]; return signal; } ]; PersonAddingViewModel Kicking off the network request

Slide 44

Slide 44 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup self.addTwitterButtonCommand = [[RACCommand alloc] initWithEnabled:self.addButtonEnabledSignal signalBlock:^RACSignal *(id input) { RACSignal *signal = [self.twitterClient infoForUsername:self.usernameSearchText]; return signal; } ]; PersonAddingViewModel Kicking off the network request

Slide 45

Slide 45 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup self.addTwitterButtonCommand = [[RACCommand alloc] initWithEnabled:self.addButtonEnabledSignal signalBlock:^RACSignal *(id input) { RACSignal *signal = [self.twitterClient infoForUsername:self.usernameSearchText]; return signal; } ]; PersonAddingViewModel Kicking off the network request We are doing exactly one thing. We don’t need
 to handle callbacks here, just start the request!

Slide 46

Slide 46 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup // subscribe to twitter network request RACSignal *twitterFetchSignal = [RACObserve(self, personAddingViewModel) flattenMap:^RACStream *(id value) { return [self.personAddingViewModel. addTwitterButtonCommand.executionSignals concat]; }]; RACSignal *UIStateSignal = [[twitterFetchSignal map:^id(id value) { return @(DetailViewState); }] startWith:@(AddingViewState)]; RAC(self, UIState) = UIStateSignal;
 RAC(self, person) = twitterFetchSignal; PersonContainerViewModel Changing the UIState upon completed request

Slide 47

Slide 47 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup // subscribe to twitter network request RACSignal *twitterFetchSignal = [RACObserve(self, personAddingViewModel) flattenMap:^RACStream *(PersonAddingViewModel *addingViewModel) { return [addingViewModel.addTwitterButtonCommand.executionSignals concat]; }]; RACSignal *UIStateSignal = [[twitterFetchSignal map:^id(id value) { return @(DetailViewState); }] startWith:@(AddingViewState)]; RAC(self, UIState) = UIStateSignal; RAC(self, person) = twitterFetchSignal; PersonContainerViewModel Changing the UIState upon completed request

Slide 48

Slide 48 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup // subscribe to twitter network request RACSignal *twitterFetchSignal = [RACObserve(self, personAddingViewModel) flattenMap:^RACStream *(id value) { return [self.personAddingViewModel. addTwitterButtonCommand.executionSignals concat]; }]; RACSignal *UIStateSignal = [[twitterFetchSignal map:^id(id value) { return @(DetailViewState); }] startWith:@(AddingViewState)]; RAC(self, UIState) = UIStateSignal; RAC(self, person) = twitterFetchSignal; PersonContainerViewModel Changing the UIState upon completed request

Slide 49

Slide 49 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup

Slide 50

Slide 50 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup - (RACSignal *)infoForUsername:(NSString *)username { ... return [[[[[self _login] deliverOn:bgScheduler] flattenMap:^RACStream *(STTwitterAPI *client) { return [self client:client fetchUserInfo:username]; }] flattenMap:^RACStream *(NSDictionary *userInfo) { return [[self imageFromURLString:userInfo[@"userInfo"]] combineLatestWith:[RACSignal return:userInfo]]; }] flattenMap:^RACStream *(RACTuple *personInfoTupel) { return [RACSignal return:[self _personFromUserInfo:personInfoTupel]]; }]; } Twitter API request Chaining network operations

Slide 51

Slide 51 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Model ViewModel View ✓

Slide 52

Slide 52 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Testing with Reactive Cocoa 2.x

Slide 53

Slide 53 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup it(@"calls the Twitter API when add button is tapped", ^{ id twitterClient = [TwitterClient new]; id twitterMock = OCMPartialMock(twitterClient); OCMStub([twitterMock infoForUsername:@“username"]) .andReturn([RACSignal return:@(YES)]); viewModel = [[PersonAddingViewModel alloc] initWithTwitterClient:twitterMock]; viewModel.usernameSearchText = @"username"; [viewModel.addTwitterButtonCommand execute:nil]; OCMVerify([twitterMock infoForUsername:@"username"]); }); Testing UI without UIKit

Slide 54

Slide 54 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Summary

Slide 55

Slide 55 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup Textfield text changed disable textfield button enable textfield disable enable button decide decide Button tapped Network error callback Button tapped Network error callback

Slide 56

Slide 56 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup • RAC introduces a vastly different programming model that can be harder to debug • RAC provides tools for writing simpler declarative code that embraces derived state


Slide 57

Slide 57 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup • MVVM plays nicely with bindings, eliminates controller complexity
 • MVVM makes it easier to write testable code

Slide 58

Slide 58 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup “[…] our intellectual powers are rather geared to master static relations and […] our powers to visualize processes evolving in time are relatively poorly developed.“  E.W. Dijkstra (And a ton of talks & blog posts that quoted him in the context of Reactive Programming)

Slide 59

Slide 59 text

@benjaminencz Functional Reactive Programming on iOS | NSMeetup • Code: https://github.com/Ben-G/PeopleCRM • Further Resources: • http://www.sprynthesis.com/2014/06/15/why- reactivecocoa/ • Functional Reactive Programming on iOS, Ash Furrow (https://leanpub.com/iosfrp/) Thank you! Thanks to Ash Furrow, Morgan Chen, Gerald Monaco, Florian Krueger and Dave Lee for input and feedback on this talk!