ReactiveCocoa (RAC): Functional Reactive Programming

Preview:

DESCRIPTION

In this presentation, Werner Ramaekers and Jan Sabbe provide an introduction to ReactiveCocoa.

Citation preview

ReactiveCocoa

Werner Ramaekers Jan Sabbe

Cocoaheads BE 12/03/2014

ReactiveCocoa (RAC) is an Objective-C framework for

Functional Reactive Programming.

It provides APIs for composing and transforming streams of

values.

Whut ?

Programming

PIN OUT

Take inputs Produce outputs

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

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

PIN OUT

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….

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….

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

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

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

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)

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.

ReactiveCocoa (RAC) is an Objective-C framework for

Functional Reactive Programming.

It provides APIs for composing and transforming streams of

values.

RAC : ReActiveCocoa

Stream Subscriber

RACStream produces a Stream existing of …

RAC : ReActiveCocoa

Stream

Next Event

Subscriber

RACStream produces a Stream existing of …

RAC : ReActiveCocoa

Stream

Next Event

Complete Event

Subscriber

RACStream produces a Stream existing of …

RAC : ReActiveCocoa

Stream

Next Event

Complete Event

Subscriber

Error Event

RACStream produces a Stream existing of …

RAC : ReActiveCocoa

Stream Subscriber

[ 1, 2, 3]

RACSequence

RAC : ReActiveCocoa

Stream Subscriber

[ 1, 2, 3]

123

RACSequence

RAC : ReActiveCocoa

Stream Subscriber

RACSignal

Client Server

RAC : ReActiveCocoa

Stream Subscriber…

JSON

RACSignal

Client Server

RAC : ReActiveCocoa

Stream Subscriber…

RACSignal

Client

listen

RAC : ReActiveCocoa

RACStream

RACSignal RACSequence

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

Details please

RACSignal!

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

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

} error:^(NSError *error){

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

} completed:^{

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

}];

RACSignal!

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

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

!

!

!

!

}];

RACSignal : combineLatest

123

C.L.

( )=,

RACStream operators•Simplifies transformations of streams into

new streams

•map, filter, fold/reduce

!

RACStream : map

MAP =

( ) =

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

[stream map:^id(id value) {

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

}];

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

RACStream : flatten map

FMAP =

( ) =

Function result = stream

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?

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

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

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

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

“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

Now, show me a real app

Station locator app

Station locator app

Station locator app

Station locator app

Core Data

Stations GPS coord Services Prices

Station locator app

Core Data

Stations GPS coord Services Prices

Driving distance

Station locator app

Prices.xml

Core Data

Stations GPS coord Services Prices

Driving distance

Station locator app

Stations.xml Prices.xml

Core Data

Stations GPS coord Services Prices

Driving distance

Easier asynchronous code

Driving distance

1

2

3 Core Data lookup in Background

current location

RAC( ) = (51.2, 5.46)

station (id=4)

flattenMap searchClosest(51.2,5.46)

flattenMap getDrivingDistance(4)

JSON

map JSON -> km

"4.5 km"

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

MVC

MVC

Scott, how do we test UIViewControllers

ViewModel patternViewController sets up bindinganimations

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

SearchService (RACSignal) searchWithCriteria

validates + delegate to service

do actual search

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

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?

RACCommand

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

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

ViewModel

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

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)

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 ?

One last thing

Thank you

Recommended