Learn You a ReactiveCocoa for Great Good

3,462
-1

Published on

Learn why ReactiveCocoa is more than just fancy KVO for Hipsters.

Published in: Technology
1 Comment
12 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,462
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
59
Comments
1
Likes
12
Embeds 0
No embeds

No notes for slide

Learn You a ReactiveCocoa for Great Good

  1. 1. Learn You A ReactiveCocoa For Great Good
  2. 2. Jason Larsen (@jarsen) Software Engineer at Instructure
  3. 3. Reactive Cocoa More Than Fancy KVO For Hipsters
  4. 4. Saying that ReactiveCocoa is just KVO/bindings is like saying that CoreData is just a SQLite wrapper.
  5. 5. Functional Programming • No state • No side effects • Immutability • First class functions
  6. 6. Reactive Programming 1 3 2 4 3 7 10
  7. 7. Reactive Programming 1 3 5 4 6 7 13
  8. 8. FRP Functional Reactive Programming
  9. 9. RAC(self, label.text) = RACObserve(self, name); Declarative tell the code what to do without telling it how to do it.
  10. 10. Reduce State (Not eliminate—Obj-C is not purely functional)
  11. 11. RACStream RACSequence (pull driven) RACSignal (push driven)
  12. 12. A RACStream is like a pipe—new values flow through it. You can subscribe to these values and then transform them as you please.
  13. 13. RACSequence Pull-Driven Stream
  14. 14. Sequences NSArray *numbers = @[ @1, @2, @3 ]; RACSequence *sequence = numbers.rac_sequence;
  15. 15. Transforming Streams
  16. 16. Map RACSequence *numbersSquared = [numbers.rac_sequence map:^id(NSNumber *number) { return @([number intValue] * [number intValue]); }];
  17. 17. Map Mapping Function @[@1, @2, @3] @[@1, @4, @9]
  18. 18. But… for loops! NSArray *numbers = @[@1,@2,@3]; NSMutableArray *mutableNumbers = [numbers mutableCopy]; for (NSNumber *number in numbers) { [mutableNumbers addObject:@([number intValue] * [number intValue])]; }
  19. 19. Lazy Evaluation NSArray *strings = @[ @"A", @"B", @"C" ]; RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { return [str stringByAppendingString:@"_"]; }]; ! sequence.head; // evaluates @"A_" sequence.tail.head; // evaluates @"B_" sequence.eagerSequence; // evaluates sequence
  20. 20. Filter RACSequence *evenNumbers = [numbers.rac_sequence filter:^BOOL(NSNumber *number) { return @([number intValue] % 2 == 0); }];
  21. 21. Filter Filtering Function @[@1, @2, @3] @[@2]
  22. 22. Fold (a.k.a. reduce) NSNumber *sum = [numbers.rac_sequence foldLeftWithStart:@0 reduce:^id(id accumulator, id value) { return @([accumulator intValue] + [value intValue]); }];
  23. 23. Fold Folding Function @[@1, @2, @3] @6 @0
  24. 24. Fold Left Sequence ! @[@1, @2, @3, @4] @[@1, @2, @3, @4] @[@1, @2, @3, @4] @[@1, @2, @3, @4] @[@1, @2, @3, @4] Accumulator ! @0 @1 @3 @6 @10
  25. 25. Combining Streams
  26. 26. Concatenating // concatenate letters at end of numbers [numbers concat:letters];
  27. 27. Flattening Sequences RACSequence *sequenceOfSequences = @[letters,numbers].rac_sequence; ! // flattening sequences concatenates them RACSequence *flattened = [sequenceOfSequences flatten];
  28. 28. RACSignal Push-Driven Stream
  29. 29. Map // set the label to always be equal to the formatted // string of “12 units”, where 12 is whatever the // current value of self.total RACSignal *signal = RACObserve(self, total); RAC(self, totalLabel.text) = [signal map:^id(NSNumber *total) { [NSString stringWithFormat:@"%i units", total]; }];
  30. 30. Filter // only set total to the even numbers in // the stream RAC(self, total) = [RACSignal(self, cell1) filter:^BOOL(NSNumber *number) { return @([number intValue] % 2 == 0); }];
  31. 31. Creating Signals [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSURLSessionDataTask *task = [self PUT:path parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) { [subscriber sendNext:responseObject]; } failure:^(NSURLSessionDataTask *task, NSError *error) { [subscriber sendError:error]; }]; return [RACDisposable disposableWithBlock:^{ [task cancel]; }]; }];
  32. 32. Combining Signals Yo dawg, I heard you like signals…
  33. 33. Sequencing // when signal 1 completes, do signal 2 [[signal doNext:^(id x) { NSLog(@"value: %@", x); }] then:^RACSignal *{ return signal2; }];
  34. 34. Merging Signals // creates a new signal that will send the // values of both signals, complete when both // are completed, and error when either errors [RACSignal merge:@[signal1, signal2]];
  35. 35. Combine Latest Values RACSignal *cell1Signal = RACObserve(self, cell1); RACSignal *cell2Signal = RACObserve(self, cell2); RAC(self, total) = [RACSignal combineLatest:@[cell1Signal, cell2Signal] reduce:^id(id cell1, id cell2){ return @([cell1 intValue] + [cell2 intValue]); }];
  36. 36. Flattening Signals RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { [subscriber sendNext:letters]; [subscriber sendNext:numbers]; [subscriber sendCompleted]; return nil; }]; ! // flattening signals merges them RACSignal *flattened = [signalOfSignals flatten];
  37. 37. Flattening & Mapping // creates multiple signals of work which // are automatically recombined, or in other words // it maps each letter to a signal using // saveEntriesForLetter: and then it merges them all. letters = @[@“a”, @“b”, @“c”] [[letters flattenMap:^(NSString *letter) { return [database saveEntriesForLetter:letter]; }] subscribeCompleted:^{ NSLog(@"All database entries saved successfully."); }];
  38. 38. Side Effects Can’t live with ‘em, can’t live without ‘em
  39. 39. What is a “side effect?” • logging • making a network request • update the UI • changing some state somewhere
  40. 40. Subscriptions [signal subscribeNext:^(id x) { // do something with each value } error:^(NSError *error) { // do something with errors } completed:^{ // do something with completed }];
  41. 41. Inject Side Effects [signal doCompleted:^{ // do some side effect after }]; ! [signal doNext:^(id x) { // some side effect here }]; ! [signal doError:^(NSError *error) { // handle error }];
  42. 42. Inject Side Effects [signal initially:^{ // do some side effect before signal }];
  43. 43. Side Effects // DO NOT DO [signal map:^id(NSString *string) { NSString *exclamation = [NSString stringWithFormat:@"%@!!!", string]; [self showAlert:exclamation]; return exclamation; }];
  44. 44. Side Effects // DO DO [[signal map:^id(NSString *string) { return [NSString stringWithFormat:@"%@!!!", string]; }] doNext:^(NSString *string) { [self showAlert:string]; }];
  45. 45. RACSubject Manual Signals
  46. 46. Use createSignal: over RACSubject when possible [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSURLSessionDataTask *task = [client PUT:path parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) { [subscriber sendNext:responseObject]; } failure:^(NSURLSessionDataTask *task, NSError *error) { [subscriber sendError:error]; }]; return [RACDisposable disposableWithBlock:^{ [task cancel]; }]; }];
  47. 47. Concurrency
  48. 48. Scenario • I want to run three networking calls and then when they are all done do something • I want to MERGE three signals and THEN do something.
  49. 49. Scenario Solution // basically: merging signals can replace dispatch groups [[RACSignal merge:@[signal1, signal2, signal3]] then:^RACSignal * { return [self someSignalToDoAfter]; }];
  50. 50. Replay/Multicasting
  51. 51. Replace Delegation • rac_signalForSelector:fromProtocol:
  52. 52. Other Cool Methods • -throttle: • -takeUntil: can be used to automatically dispose of a subscription when an event occurs (like a "Cancel" button being pressed in the UI). • -setNameWithFormat: for debugging • -logNext, -logError, -logCompleted, -logAll automatically log signal events as they occur, and include the name of the signal in the messages. This can be used to conveniently inspect a signal in real-time.
  53. 53. Your hammer 99% of the time • -subscribeNext:error:completed: • -doNext: / -doError: / -doCompleted: • -map: • -filter: • -concat: • -flattenMap: • -then: • +merge: • +combineLatest:reduce: • -switchToLatest:
  54. 54. We Want More!
  55. 55. Specifically, read DesignGuidelines.md and BasicOperators.md https://github.com/ReactiveCocoa/ ReactiveCocoa/tree/master/Documentation
  56. 56. https://leanpub.com/iosfrp If you’re into books, this one’s decent.
  57. 57. https://github.com/ReactiveCocoa/ ReactiveViewModel
  58. 58. Github’s Obj-C API Lib https://github.com/octokit/octokit.objc ! (and Instructure’s API Lib modeled after it) https://github.com/instructure/canvaskit
  59. 59. Don’t Cross the Streams
  60. 60. Type Safety A word of warning
  61. 61. Non KVO Compliance like textLabel.text
  62. 62. viewDidLoad Bloat break stuff up into setupMethods
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×