Top Banner
ReactiveCocoa Werner Ramaekers Jan Sabbe Cocoaheads BE 12/03/2014
64

Reactive cocoa cocoaheadsbe_2014

May 11, 2015

Download

Technology

Presentation on ReactiveCocoa given at the CocoaHeadsBE presentation on March 12th 2014.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Reactive cocoa cocoaheadsbe_2014

ReactiveCocoa

Werner Ramaekers Jan Sabbe

Cocoaheads BE 12/03/2014

Page 2: Reactive cocoa cocoaheadsbe_2014
Page 3: Reactive cocoa cocoaheadsbe_2014
Page 4: Reactive cocoa cocoaheadsbe_2014

ReactiveCocoa (RAC) is an Objective-C framework for

Functional Reactive Programming.

It provides APIs for composing and transforming streams of

values.

Page 5: Reactive cocoa cocoaheadsbe_2014

Whut ?

Page 6: Reactive cocoa cocoaheadsbe_2014

Programming

PIN OUT

Take inputs Produce outputs

Page 7: Reactive cocoa cocoaheadsbe_2014

Imperative programming•int x; int y = 1; int z = 2;

•x = y + z; //x == 3; y == 1; z == 2; !

PIN OUT

Page 8: Reactive cocoa cocoaheadsbe_2014

Imperative programming•int x; int y = 1; int z = 2;

•x = y + z; //x == 3; y == 1; z == 2; !

PIN OUT

•y = 2;

•NSLog(@“%i”, x); //-> ‘3’ !

# Input changed but output did NOT because it was declared earlier….

Page 9: Reactive cocoa cocoaheadsbe_2014

Imperative programming•int x; int y = 1; int z = 2;

•x = y + z; //x == 3; y == 1; z == 2; !

PIN OUT

STATE •y = 2;

•NSLog(@“%i”, x); //-> ‘3’ !

# Input changed but output did NOT because it was declared earlier….

Page 10: Reactive cocoa cocoaheadsbe_2014

Declaractive programming

•The developer describes the problem domain in a declarative way, !

•The focus is NOT on state !

•Focus is on : •data flow

•propagation of change

Page 11: Reactive cocoa cocoaheadsbe_2014

Functional Programming

•is one way of declarative programming. !

•Functional programming..

•deemphasizes state & mutable data •emphasizes functions that produce

outputs based on inputs : z = f(x, y)

PIN OUT

Page 12: Reactive cocoa cocoaheadsbe_2014

Reactive Programming

•also is a way of declarative programming !

•Reactive Programming .. •dynamically links the program’s behaviour

to its continuously updating data stream

PIN OUT

Page 13: Reactive cocoa cocoaheadsbe_2014

Reactive Programming

•also is a way of declarative programming !

•Reactive Programming .. •dynamically links the program’s behaviour

to its continuously updating data stream

PIN OUT

•E.g. : a spreadsheet application where

•cell A1 (= B1 + C1)

Page 14: Reactive cocoa cocoaheadsbe_2014

Functional Reactive Programming

•.. combines the FUNCTIONAL and REACTIVE programming styles. !

•.. links functions that observe continuous and dynamic streams of data (inputs) to the program’s behaviour (outputs) in real time.

Page 15: Reactive cocoa cocoaheadsbe_2014

ReactiveCocoa (RAC) is an Objective-C framework for

Functional Reactive Programming.

It provides APIs for composing and transforming streams of

values.

Page 16: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber

RACStream produces a Stream existing of …

Page 17: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream

Next Event

Subscriber

RACStream produces a Stream existing of …

Page 18: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream

Next Event

Complete Event

Subscriber

RACStream produces a Stream existing of …

Page 19: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream

Next Event

Complete Event

Subscriber

Error Event

RACStream produces a Stream existing of …

Page 20: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber

[ 1, 2, 3]

RACSequence

Page 21: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber

[ 1, 2, 3]

123

RACSequence

Page 22: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber

RACSignal

Client Server

Page 23: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber…

JSON

RACSignal

Client Server

Page 24: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

Stream Subscriber…

RACSignal

Client

listen

Page 25: Reactive cocoa cocoaheadsbe_2014

RAC : ReActiveCocoa

RACStream

RACSignal RACSequence

Page 26: Reactive cocoa cocoaheadsbe_2014

ReactiveCocoa •RACStream

•abstract class

•represents some sequential flow of data

•RACSignal (cold)

•push-driven data stream of future values

•cfr Network call

•must be subscribed to in order to access data

•RACSequence

•pull-driven data stream

•similar to Cocoa collection classes (NSArray, ..)

•but values are evaluated lazily - only when needed

Page 27: Reactive cocoa cocoaheadsbe_2014

Details please

Page 28: Reactive cocoa cocoaheadsbe_2014

RACSignal!

[self.textField.rac_textSignal subscribeNext:^(id x) {

NSLog(@“New value : %@“, x);

} error:^(NSError *error){

NSLog(@“Error : %@“, error);

} completed:^{

NSLog(@“That’s all folks !”);

}];

Page 29: Reactive cocoa cocoaheadsbe_2014

RACSignal!

[self.textField.rac_textSignal subscribeNext:^(id x) {

NSLog(@“New value : %@“, x);

!

!

!

!

}];

Page 30: Reactive cocoa cocoaheadsbe_2014

RACSignal : combineLatest

123

C.L.

( )=,

Page 31: Reactive cocoa cocoaheadsbe_2014

RACStream operators•Simplifies transformations of streams into

new streams

•map, filter, fold/reduce

!

Page 32: Reactive cocoa cocoaheadsbe_2014

RACStream : map

MAP =

( ) =

RACSequence *stream = [@[@(1), @(2), @(3)] rac_sequence];

[stream map:^id(id value) {

return @(pow([value integerValue], 2));

}];

NSLog(@“%@“, [stream array]);

Page 33: Reactive cocoa cocoaheadsbe_2014

RACStream : flatten map

FMAP =

( ) =

Function result = stream

Page 34: Reactive cocoa cocoaheadsbe_2014

RACStream : filter

RACSequence *stream = [@[@(1), @(2), @(3)]; rac_sequence];

NSLog(@“%@“,

[[[array rac_sequence] filter:^BOOL(id value){

return [value integerValue] % 2 == 0;

}] array]

);

Filter =

( ) = True?

Page 35: Reactive cocoa cocoaheadsbe_2014
Page 36: Reactive cocoa cocoaheadsbe_2014

Validating input the “old” way

#pragma mark - UITextFieldDelegate !- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { self.createButton.enabled = [self isFormValid]; ! return YES; }

Page 37: Reactive cocoa cocoaheadsbe_2014

Validating input the “old” way

#pragma mark - UITextFieldDelegate !- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { self.createButton.enabled = [self isFormValid]; ! return YES; }

- (BOOL)isFormValid { return [self.usernameField.text length] > 0 && [self.emailField.text length] > 0 && [self.passwordField.text length] > 0 && [self.passwordField.text isEqual:self.passwordVerificationField.text]; } … …… …

Page 38: Reactive cocoa cocoaheadsbe_2014
Page 39: Reactive cocoa cocoaheadsbe_2014

Validating input the “RAC” wayRAC(self.createButton, enabled) = ! [RACSignal combineLatest:@[ self.usernameTF.rac_textSignal, self.emailFieldTF.rac_textSignal, self.passwordFieldTF.rac_textSignal, self.passwordVerificationFieldTF.rac_textSignal ] reduce:^(NSString *username, NSString *email, NSString *password, NSString *passwordVerification) { return @([username length] > 0 && [email length] > 0 && [password length] > 8 && [password isEqual:passwordVerification]); }];

Page 40: Reactive cocoa cocoaheadsbe_2014

Validating input the “RAC” wayRAC(self.createButton, enabled) = ! [RACSignal combineLatest:@[ self.usernameTF.rac_textSignal, self.emailFieldTF.rac_textSignal, self.passwordFieldTF.rac_textSignal, self.passwordVerificationFieldTF.rac_textSignal ] reduce:^(NSString *username, NSString *email, NSString *password, NSString *passwordVerification) { return @([username length] > 0 && [email length] > 0 && [password length] > 8 && [password isEqual:passwordVerification]); }];

Page 41: Reactive cocoa cocoaheadsbe_2014

“not so fast cowboy .. “•RAC(object, key path) is a macro

•creates a one-way binding

•ex. bind “enabled” property of a UIButton to a signal

Page 42: Reactive cocoa cocoaheadsbe_2014

Now, show me a real app

Page 43: Reactive cocoa cocoaheadsbe_2014

Station locator app

Page 44: Reactive cocoa cocoaheadsbe_2014

Station locator app

Page 45: Reactive cocoa cocoaheadsbe_2014

Station locator app

Page 46: Reactive cocoa cocoaheadsbe_2014

Station locator app

Core Data

Stations GPS coord Services Prices

Page 47: Reactive cocoa cocoaheadsbe_2014

Station locator app

Core Data

Stations GPS coord Services Prices

Driving distance

Page 48: Reactive cocoa cocoaheadsbe_2014

Station locator app

Prices.xml

Core Data

Stations GPS coord Services Prices

Driving distance

Page 49: Reactive cocoa cocoaheadsbe_2014

Station locator app

Stations.xml Prices.xml

Core Data

Stations GPS coord Services Prices

Driving distance

Page 50: Reactive cocoa cocoaheadsbe_2014

Easier asynchronous code

Driving distance

1

2

3 Core Data lookup in Background

current location

Page 51: Reactive cocoa cocoaheadsbe_2014

RAC( ) = (51.2, 5.46)

station (id=4)

flattenMap searchClosest(51.2,5.46)

flattenMap getDrivingDistance(4)

JSON

map JSON -> km

"4.5 km"

Page 52: Reactive cocoa cocoaheadsbe_2014

Sort stations by driving distance

station (id=1)

station (id=3)

station (id=2)

getDrivingDistance

combineLatest sort [3,1,2]

fetching driving distances concurrentlywhen all available, sort

Page 53: Reactive cocoa cocoaheadsbe_2014

MVC

Page 54: Reactive cocoa cocoaheadsbe_2014

MVC

Page 55: Reactive cocoa cocoaheadsbe_2014

Scott, how do we test UIViewControllers

Page 56: Reactive cocoa cocoaheadsbe_2014

ViewModel patternViewController sets up bindinganimations

ViewModel (BOOL) shop (BOOL) bread (RACCommand) search

SearchService (RACSignal) searchWithCriteria

validates + delegate to service

do actual search

Page 57: Reactive cocoa cocoaheadsbe_2014

ViewModel patternViewController sets up bindinganimations

ViewModel (BOOL) shop (BOOL) bread (RACCommand) search

SearchService (RACSignal) searchWithCriteria

mock out the real search service

do search against in memory DB

easily testable

harder to test

hard to test but trivial

Page 58: Reactive cocoa cocoaheadsbe_2014

ViewModel patternViewController sets up bindinganimations

ViewModel (BOOL) shop (BOOL) bread (RACCommand) search

SearchService (RACSignal) searchWithCriteria

validates + delegate to service

do actual search

what is this?

Page 59: Reactive cocoa cocoaheadsbe_2014

RACCommand

while getting current location, searching stations, sort by driving distance,... show activity indicator

[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id sender) { return [self createSearchSignal]; }];

ViewModel

Page 60: Reactive cocoa cocoaheadsbe_2014

RACCommand

while getting current location, searching stations, sort by driving distance,... show activity indicator

[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id sender) { return [self createSearchSignal]; }];

ViewModel

ViewControllerself.searchButton.rac_command = self.viewModel.searchCommand; RAC(self, spinner.hidden) = [self.viewModel.searchCommand.executing not];

Page 61: Reactive cocoa cocoaheadsbe_2014

ReactiveCocoa …•keeps the app focused on representing data

& UX

•can reduce complexity bc you don’t need to worry about ‘state’ so much

•keeps related code close together (via blocks)

•helps to make your code more testable (via MVVM)

Page 62: Reactive cocoa cocoaheadsbe_2014

but ReactiveCocoa …•introduces another way of thinking.

•adds another learning curve on top of Cocoa !

•is a framework by GitHub and not Apple !

•will Apple "sherlock" ReactiveCocoa ?

Page 63: Reactive cocoa cocoaheadsbe_2014

One last thing

Page 64: Reactive cocoa cocoaheadsbe_2014

Thank you