learn you a reactivecocoa for great good

63
Learn You A ReactiveCocoa For Great Good

Upload: jason-larsen

Post on 11-May-2015

1.915 views

Category:

Technology


6 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Learn You a ReactiveCocoa for Great Good

Learn You A ReactiveCocoaFor Great Good

Page 2: Learn You a ReactiveCocoa for Great Good

Jason Larsen (@jarsen)Software Engineer at Instructure

Page 3: Learn You a ReactiveCocoa for Great Good

Reactive CocoaMore Than Fancy KVO For Hipsters

Page 4: Learn You a ReactiveCocoa for Great Good

Saying that ReactiveCocoa is just KVO/bindings is like saying that CoreData is just a SQLite

wrapper.

Page 5: Learn You a ReactiveCocoa for Great Good

Functional Programming

• No state

• No side effects

• Immutability

• First class functions

Page 6: Learn You a ReactiveCocoa for Great Good

Reactive Programming

1 3

2 4

3 7 10

Page 7: Learn You a ReactiveCocoa for Great Good

Reactive Programming

1 3

5 4

6 7 13

Page 8: Learn You a ReactiveCocoa for Great Good

FRPFunctional Reactive Programming

Page 9: Learn You a ReactiveCocoa for Great Good

RAC(self, label.text) = RACObserve(self, name);

Declarativetell the code what to do without telling it how to do it.

Page 10: Learn You a ReactiveCocoa for Great Good

Reduce State(Not eliminate—Obj-C is not purely functional)

Page 11: Learn You a ReactiveCocoa for Great Good

RACStream

RACSequence (pull driven)

RACSignal (push driven)

Page 12: Learn You a ReactiveCocoa for Great Good

A RACStream is like a pipe—new values flow through it. You can subscribe to these values

and then transform them as you please.

Page 13: Learn You a ReactiveCocoa for Great Good

RACSequencePull-Driven Stream

Page 14: Learn You a ReactiveCocoa for Great Good

Sequences

NSArray *numbers = @[ @1, @2, @3 ]; RACSequence *sequence = numbers.rac_sequence;

Page 15: Learn You a ReactiveCocoa for Great Good

Transforming Streams

Page 16: Learn You a ReactiveCocoa for Great Good

Map

RACSequence *numbersSquared = [numbers.rac_sequence map:^id(NSNumber *number) { return @([number intValue] * [number intValue]); }];

Page 17: Learn You a ReactiveCocoa for Great Good

Map

Mapping Function@[@1, @2, @3] @[@1, @4, @9]

Page 18: Learn You a ReactiveCocoa for Great Good

But… for loops!

NSArray *numbers = @[@1,@2,@3]; NSMutableArray *mutableNumbers = [numbers mutableCopy]; for (NSNumber *number in numbers) { [mutableNumbers addObject:@([number intValue] * [number intValue])]; }

Page 19: Learn You a ReactiveCocoa for Great Good

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

Page 20: Learn You a ReactiveCocoa for Great Good

Filter

RACSequence *evenNumbers = [numbers.rac_sequence filter:^BOOL(NSNumber *number) { return @([number intValue] % 2 == 0); }];

Page 21: Learn You a ReactiveCocoa for Great Good

Filter

Filtering Function@[@1, @2, @3] @[@2]

Page 22: Learn You a ReactiveCocoa for Great Good

Fold (a.k.a. reduce)

NSNumber *sum = [numbers.rac_sequence foldLeftWithStart:@0 reduce:^id(id accumulator, id value) { return @([accumulator intValue] + [value intValue]); }];

Page 23: Learn You a ReactiveCocoa for Great Good

Fold

Folding Function@[@1, @2, @3] @6

@0

Page 24: Learn You a ReactiveCocoa for Great Good

Fold LeftSequence

!@[@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

Page 25: Learn You a ReactiveCocoa for Great Good

Combining Streams

Page 26: Learn You a ReactiveCocoa for Great Good

Concatenating

// concatenate letters at end of numbers [numbers concat:letters];

Page 27: Learn You a ReactiveCocoa for Great Good

Flattening Sequences

RACSequence *sequenceOfSequences = @[letters,numbers].rac_sequence; !// flattening sequences concatenates them RACSequence *flattened = [sequenceOfSequences flatten];

Page 28: Learn You a ReactiveCocoa for Great Good

RACSignalPush-Driven Stream

Page 29: Learn You a ReactiveCocoa for Great Good

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]; }];

Page 30: Learn You a ReactiveCocoa for Great Good

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); }];

Page 31: Learn You a ReactiveCocoa for Great Good

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]; }]; }];

Page 32: Learn You a ReactiveCocoa for Great Good

Combining SignalsYo dawg, I heard you like signals…

Page 33: Learn You a ReactiveCocoa for Great Good

Sequencing

// when signal 1 completes, do signal 2 [[signal doNext:^(id x) { NSLog(@"value: %@", x); }] then:^RACSignal *{ return signal2; }];

Page 34: Learn You a ReactiveCocoa for Great Good

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]];

Page 35: Learn You a ReactiveCocoa for Great Good

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]); }];

Page 36: Learn You a ReactiveCocoa for Great Good

Flattening SignalsRACSignal *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];

Page 37: Learn You a ReactiveCocoa for Great Good

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."); }];

Page 38: Learn You a ReactiveCocoa for Great Good

Side EffectsCan’t live with ‘em, can’t live without ‘em

Page 39: Learn You a ReactiveCocoa for Great Good

What is a “side effect?”

• logging

• making a network request

• update the UI

• changing some state somewhere

Page 40: Learn You a ReactiveCocoa for Great Good

Subscriptions

[signal subscribeNext:^(id x) { // do something with each value } error:^(NSError *error) { // do something with errors } completed:^{ // do something with completed }];

Page 41: Learn You a ReactiveCocoa for Great Good

Inject Side Effects[signal doCompleted:^{ // do some side effect after }]; ![signal doNext:^(id x) { // some side effect here }]; ![signal doError:^(NSError *error) { // handle error }];

Page 42: Learn You a ReactiveCocoa for Great Good

Inject Side Effects

[signal initially:^{ // do some side effect before signal }];

Page 43: Learn You a ReactiveCocoa for Great Good

Side Effects

// DO NOT DO [signal map:^id(NSString *string) { NSString *exclamation = [NSString stringWithFormat:@"%@!!!", string]; [self showAlert:exclamation]; return exclamation; }];

Page 44: Learn You a ReactiveCocoa for Great Good

Side Effects

// DO DO [[signal map:^id(NSString *string) { return [NSString stringWithFormat:@"%@!!!", string]; }] doNext:^(NSString *string) { [self showAlert:string]; }];

Page 45: Learn You a ReactiveCocoa for Great Good

RACSubjectManual Signals

Page 46: Learn You a ReactiveCocoa for Great Good

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]; }]; }];

Page 47: Learn You a ReactiveCocoa for Great Good

Concurrency

Page 48: Learn You a ReactiveCocoa for Great Good

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.

Page 49: Learn You a ReactiveCocoa for Great Good

Scenario Solution

// basically: merging signals can replace dispatch groups [[RACSignal merge:@[signal1, signal2, signal3]] then:^RACSignal * { return [self someSignalToDoAfter]; }];

Page 50: Learn You a ReactiveCocoa for Great Good

Replay/Multicasting

Page 51: Learn You a ReactiveCocoa for Great Good

Replace Delegation

• rac_signalForSelector:fromProtocol:

Page 52: Learn You a ReactiveCocoa for Great Good

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.

Page 53: Learn You a ReactiveCocoa for Great Good
Page 54: Learn You a ReactiveCocoa for Great Good

Your hammer 99% of the time

• -subscribeNext:error:completed:

• -doNext: / -doError: / -doCompleted:

• -map:

• -filter:

• -concat:

• -flattenMap:

• -then:

• +merge:

• +combineLatest:reduce:

• -switchToLatest:

Page 55: Learn You a ReactiveCocoa for Great Good

We Want More!

Page 56: Learn You a ReactiveCocoa for Great Good

Specifically, read DesignGuidelines.md and BasicOperators.md

https://github.com/ReactiveCocoa/ReactiveCocoa/tree/master/Documentation

Page 57: Learn You a ReactiveCocoa for Great Good

https://leanpub.com/iosfrp If you’re into books, this one’s decent.

Page 58: Learn You a ReactiveCocoa for Great Good

https://github.com/ReactiveCocoa/ReactiveViewModel

Page 59: Learn You a ReactiveCocoa for Great Good

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

Page 60: Learn You a ReactiveCocoa for Great Good

Don’t Cross the Streams

Page 61: Learn You a ReactiveCocoa for Great Good

Type SafetyA word of warning

Page 62: Learn You a ReactiveCocoa for Great Good

Non KVO Compliancelike textLabel.text

Page 63: Learn You a ReactiveCocoa for Great Good

viewDidLoad Bloatbreak stuff up into setupMethods