Slide 1

Slide 1 text

What the FRP? Using Reactive Cocoa • NSMeetup July

Slide 2

Slide 2 text

Jay Baird • @skatterbean Work at Rackspace Indie iOS developer

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Not an expert, just an enthusiast.

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

The dreaded server list view.

Slide 7

Slide 7 text

Regions Servers Images Sizes

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

make dependencies explicit

Slide 10

Slide 10 text

values can change over time

Slide 11

Slide 11 text

We wait for I/O. We wait for the disk. We wait for users.

Slide 12

Slide 12 text

Data Flow

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

a graph of signals that propagate change Reactive Programming!

Slide 15

Slide 15 text

Reactive Cocoa a github joint • https://github.com/ReactiveCocoa

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

The Players

Slide 19

Slide 19 text

RACStream •Any series of object values available immediately or in the future. •Monads and strife.

Slide 20

Slide 20 text

• Protocol any object can adopt to receive values from a signal. • Use RACSignal’s - subscribeNext:error:completed: convenience methods

Slide 21

Slide 21 text

RACSignal • Push driven stream • As work is completed values are sent on the signal to its subscribers • Sends next, error and completed events • Can sends any number of next events, followed by one error or completed (but not both)

Slide 22

Slide 22 text

Example

Slide 23

Slide 23 text

Binding

Slide 24

Slide 24 text

RAC(self.image) = [[[client getImage:self.imageId] catch:^RACSignal *(NSError *error) { return [RACSignal empty]; // swallow the error }] map:^(NSDictionary *imageDict) { return [Image fromJSON:imageDict[@"image"]]; }];

Slide 25

Slide 25 text

RACSubject • Think of it as RACMutableSignal • provides sendNext:, sendError: and sendCompleted methods • Crazy useful for bridging other code, e.g. AFNetworking

Slide 26

Slide 26 text

Example

Slide 27

Slide 27 text

Timers

Slide 28

Slide 28 text

RACSubject *pollTimer = [RACSubject subject]; [[[RACSignal interval:60.0f] takeUntil:[pollTimer doNext:^(id x) { NSLog(@"Server poll cancelled"); }]] subscribeNext:^(id x) { NSLog(@"Tick."); } error:^(NSError *error) { NSLog(@"Handle the error condition"); } completed:^{ NSLog(@"Timer completed"); }]; // Cancels the timer. // A unit represents an empty value [pollTimer sendNext:[RACUnit defaultUnit]];

Slide 29

Slide 29 text

The dreaded server list view

Slide 30

Slide 30 text

RACSubject *subject = [RACSubject subject]; // +flavors returns a RACSignal with flavors // +images returns a RACSignal with images [[RACSignal combineLatest:@[[Flavor flavors], [Image images]]] subscribeError:^(NSError *error) { [subject sendError:error]; } completed:^{ [[client getServers] subscribeNext:^(NSArray *serverJSON) { // Process server JSON [subject sendCompleted]; } error:^(NSError *error) { [subject sendError:error]; } completed:^{ [subject sendCompleted]; }]; }]; return subject;

Slide 31

Slide 31 text

RACMulticastConnection • Signals start their work on each new subscription – great for isolating work • but bad if you have heavy side effects • Made using -multicast: on RACSignal • Creates one and only one subscription

Slide 32

Slide 32 text

Example

Slide 33

Slide 33 text

Authentication

Slide 34

Slide 34 text

RACSignal *deferredToken = [RACSignal defer:^{ return [self authWithUsername:username key:password]; }]; _tokenConnection = [deferredToken multicast:[RACReplaySubject subject]]; - (RACSignal *)withAuthToken { [_tokenConnection connect]; return _tokenConnection.signal; } - (RACSignal *)getServers { return [[self withAuthToken] flattenMap:^RACSignal *(NSString *token) { return [self enqueueRequestWithMethod:@"GET" endpoints:self.computeEndpoints path:@"/servers/detail" parameters:nil]; }]; }

Slide 35

Slide 35 text

a GUI example

Slide 36

Slide 36 text

KVO

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

NSArray *fields = @[RACAble(self.password), RACAble(self.passwordConfirmation)]; RAC(self.createEnabled) = [RACSignal combineLatest:fields reduce:^(NSString *password, NSString *passwordConfirm) { return [passwordConfirm isEqualToString:password]; }];

Slide 39

Slide 39 text

Notification Signals

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

RAC(self.logInButton.enabled) = [RACSignal combineLatest:@[ self.usernameTextField.rac_textSignal, self.passwordTextField.rac_textSignal] reduce:^(NSString *user, NSString *pass) { return (user.length > 0 && pass.length > 0); }];

Slide 42

Slide 42 text

Control Events

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

RACSignal *deleteSignal = [button rac_signalForControlEvents:UIControlEventTouchUpInside]; [deleteSignal subscribeNext:^(UIButton *sender) { [[self.server delete] subscribeCompleted:deleteServer]; }];

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content