Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Reactive Programming on iOS

Functional Reactive Programming on iOS

Most iOS applications make heavy use of mutable state, which increases code complexity and results in software that is expensive to maintain. Functional Reactive Programming provides us with a toolset that reduces our dependency on mutable state.

This talk will discuss the philosophy of Functional Reactive Programming and show how ReactiveCocoa can be used to write maintainable and testable UI and Networking code. It will show that FRP offers some very pragmatic approaches to fix common issues in your codebase.

Benjamin Encz

April 01, 2015
Tweet

More Decks by Benjamin Encz

Other Decks in Programming

Transcript

  1. @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
  2. @benjaminencz Functional Reactive Programming on iOS | NSMeetup • State

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

    Default Loading 3 High level states 9 possible invalid states!
  4. @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
  5. @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
  6. @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
  7. @benjaminencz Functional Reactive Programming on iOS | NSMeetup • Callbacks

    • Delegate methods • KVO / Property overriding Signals } Values over time
  8. @benjaminencz Functional Reactive Programming on iOS | NSMeetup RACSignal sendNext:

    send Completed: send Error: Val 1 Val 2 Subscriber Subscriber Subscriber
  9. @benjaminencz Functional Reactive Programming on iOS | NSMeetup Textfield text

    changed user.email “h” “he” “hel” Bind RAC(self.user, username) = self.usernameTextfield.rac_textSignal
  10. @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;
  11. @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
  12. @benjaminencz Functional Reactive Programming on iOS | NSMeetup Textfield text

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

    Signals Subscriber Signal 1 Signal 2 Subscriber Val 1 Val 1
  14. @benjaminencz Functional Reactive Programming on iOS | NSMeetup Model ViewModel

    View Stores View state, communicates
 with model Stores model state, provides business logic Bindings
  15. @benjaminencz Functional Reactive Programming on iOS | NSMeetup PersonAddingView* PersonAddingViewModel

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

    self.viewModel.addTwitterButtonCommand; RAC(self.usernameTextfield, enabled) = self.viewModel.textFieldEnabledSignal; PersonAddingView Initialization
  17. @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
  18. @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
  19. @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
  20. @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!
  21. @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
  22. @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
  23. @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
  24. @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
  25. @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
  26. @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
  27. @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

  28. @benjaminencz Functional Reactive Programming on iOS | NSMeetup • MVVM

    plays nicely with bindings, eliminates controller complexity
 • MVVM makes it easier to write testable code
  29. @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)
  30. @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!