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

Everyday ReactiveCocoa

Everyday ReactiveCocoa

Overview of my thoughts and experience using ReactiveCocoa. Presented at Cocoaheads Brisbane Meetup held on 1 April 2014.

Rob Pearson

April 02, 2014
Tweet

More Decks by Rob Pearson

Other Decks in Programming

Transcript

  1. Functional Programming In functional programming, programs are executed by evaluating

    expressions ... avoids using mutable state. - Haskell Wiki http://haskell.org/haskellwiki/Functional_programming
  2. Inputs and Outputs "Programs take input and produce output. The

    output is the result of doing something with the input. Input, transform, output, done." Josh Abernathy - http://blog.maybeapps.com/post/42894317939/input-and-output
  3. Inputs — Keyboard (text) input — Click/Touch input — Timers

    (intervals) — GPS location changes — Resources from web services ...
  4. Reacting to Signals via subscriptions [[self.transitLocationRepository getTransitLocations] subscribeNext:^(MPXTransitLocation *transitLocation) {

    // Do something interesting with transit location } error:^(NSError *error) { // Error handling } completed:^{ // Completion handling }];
  5. Transit App Example self.canAddNewEverydayTransitTripSignal = [RACSignal combineLatest:@[ self.selectedDepartingStationSignal, self.selectedArrivingStationSignal ]

    reduce: ^id(MPXTransitLocation *departingTransitLocation, MPXTransitLocation *arrivingTransitLocation) { BOOL isValid = NO; if (departingTransitLocation != nil && arrivingTransitLocation != nil) { isValid = YES; } return @(isValid); }];
  6. Creating Signals - (RACSignal *)runReactiveDatabaseFetchBlock:(FMResultSet *(^)(FMDatabase *database))databaseFetchBlock andMapObjects:(id (^)(FMResultSet *

    resultSet))mapObjectBlock { // TODO: Rename as this isn't ideal. NSParameterAssert(databaseFetchBlock != nil); NSParameterAssert(mapObjectBlock != nil); RACSignal *databaseFetchSignal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) { __block FMResultSet *resultSet = nil; [self.databaseQueue inDatabase:^(FMDatabase *database) { resultSet = databaseFetchBlock(database); while ([resultSet next]) { id object = mapObjectBlock(resultSet); if (object != nil) { [subscriber sendNext:object]; } } }]; [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ if (resultSet != nil){ [resultSet close]; } }]; }]; return databaseFetchSignal; }
  7. Key Value Observing // Bind Transit Trips to Table View

    [RACObserve(self.viewModel, everydayTransitTrips) subscribeNext:^(id x) { @strongify(self); // Refresh 'Transit Trips' MCSimpleTableSection if needed ... [self.tableView reloadData]; }];
  8. Bind a signal to a property RACSignal *titleSignal = [RACObserve(self.viewModel,

    title) distinctUntilChanged]; RAC(self, title) = [titleSignal deliverOn:[RACScheduler mainThreadScheduler]];
  9. React to Delegates/Selectors [[self rac_signalForSelector:@selector(searchBar:textDidChange:) fromProtocol:@protocol(UISearchBarDelegate)] subscribeNext:^(RACTuple *value) { @strongify(self);

    UISearchBar *searchBar = value.first; if (searchBar == self.departingLocationsSearchBar) { [self.viewModel filterDepartingLocationsByName:self.departingLocationsSearchBar.text]; } else { [self.viewModel filterArrivingLocationsByName:self.arrivingLocationsSearchBar.text]; } }];
  10. React to Button Commands // Add Button self.addButton.rac_command = [[RACCommand

    alloc] initWithEnabled:self.viewModel.canAddNewEverydayTransitTripSignal signalBlock:^RACSignal *(id input) { // Add New Transit Trip }]; [self.addButton.rac_command.errors subscribeNext:^(id x) { // TODO: Add support for error handling from the command. }];
  11. React to Control Events MCSimpleTableCell *everydayTransitTripCell = [[MCSimpleTableCell alloc] init];

    everydayTransitTripCell.cellIdentifier = @"cellwithswitch"; everydayTransitTripCell.configureBlock = ^(MCSimpleTableCell *cell, UITableViewCell *tableCell) { tableCell.textLabel.text = trip.tripDescription; UISwitch *control = [[UISwitch alloc] initWithFrame:CGRectZero]; control.on = trip.isEnabled; tableCell.accessoryView = control; tableCell.selectionStyle = UITableViewCellSelectionStyleNone; [[control rac_signalForControlEvents:UIControlEventValueChanged] subscribeNext:^(id x) { @strongify(self); [self.viewModel toggleTripEnablementWithEverydayTransitTripId:trip.everydayTransitTripId]; }]; }; [everydayTransitTripsSection addCell:everydayTransitTripCell];
  12. Transit App Dashboard Inputs: * Transit Trip Times * Location

    Updates * Time Updates Output: * Next Transit Service based on time/location
  13. Protips — Start by reading IntroToRx.com — Start small and

    iterate. — Asks questions by opening issues at http:// github.com/ReactiveCocoa/
  14. References — Github Repo: http://github.com/ReactiveCocoa/ — Ray Wenderlich Tutorial: https://bit.ly/1rXA31Y

    — Big Nerd Ranch Tutorial: https://bit.ly/1mp04mI — FRP on iOS by Ash Furrow: https://leanpub.com/ iosfrp — Brent Simmons on ReactiveCocoa: https://bit.ly/ PcyjCL