Top Banner
ReactiveCocoa Functional Reactive Programming
23

Functional Reactive Programming (CocoaHeads Bratislava)

Jul 18, 2015

Download

Technology

Michal Grman
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: Functional Reactive Programming (CocoaHeads Bratislava)

ReactiveCocoaFunctional Reactive Programming

Page 2: Functional Reactive Programming (CocoaHeads Bratislava)

Philosophy

!

• Imperative programming

• Functional reactive programming (FRP)

Page 3: Functional Reactive Programming (CocoaHeads Bratislava)

Imperative

• You tell to the computer what to do, how to do it

• Modify program’s states

• Flow control (loops, conditions, methods)

• Manipulation via instances of structs or classes

Page 4: Functional Reactive Programming (CocoaHeads Bratislava)

FRP what is it?• Functional programming

• no states, no side-effects, immutable, first-class objects

• Reactive programming (spreadsheet example)

!

!

• Functional + Reactive = FRP paradigms

A(B+C) B C

2 1 1

Page 5: Functional Reactive Programming (CocoaHeads Bratislava)

FRP frameworks• Objective C, Swift

• ReactiveCocoa (Justin Spahr-Summers, Josh Abernathy)

• Java

• RxJava (Netflix)

• Unity3D

• UniRx (Yoshifumi Kawai)

Page 6: Functional Reactive Programming (CocoaHeads Bratislava)

ReactiveCocoa Building blocks (RACStream)

• RACStream (new value flows)

• Subscriptions (subscribe to receive values)

• Transformations (transform values as you want)

• RACStream

• RACSequence

• RACSignal

Page 7: Functional Reactive Programming (CocoaHeads Bratislava)

RACStream

• Pull-driven stream in pipe (ask for data)

• RACSequence (lazy-loaded collections)

• Push-driven stream in pipe (data in future)

• RACSignal (events: next, complete, error)

Page 8: Functional Reactive Programming (CocoaHeads Bratislava)

RACSequence• Conventional

NSArray *strings = [@"A B C D E F G H I" componentsSeparatedByString:@" “]; NSMutableArray mutableStrings = [NSMutableArray array]; for (NSString* str in strings) {

[mutableStrings addObject:[str stringByAppendingString:str]]; } !

// Contains: AA BB CC DD EE FF GG HH II

Page 9: Functional Reactive Programming (CocoaHeads Bratislava)

• Filter collection

RACSequence• Transform collection

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *mapped = [letters map:^(NSString *value) { return [value stringByAppendingString:value]; }]; !// Contains: AA BB CC DD EE FF GG HH II

RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) { return (value.intValue % 2) == 0; }]; !// Contains: 2 4 6 8

Page 10: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal• Cold - Lazy, send values only when somebody

subscribed to them, repeated work upon each subscription

• Multicasting - shared signal upon multiple subscriptions

• Hot - rare, immediate work needs to be done

Page 11: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal

• Conventional- (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]; } !#pragma mark - UITextFieldDelegate !- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { self.createButton.enabled = [self isFormValid]; ! return YES; }

Page 12: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal

• RAC basedRACSignal *formValid = [RACSignal combineLatest:@[ self.username.rac_textSignal, self.emailField.rac_textSignal, self.passwordField.rac_textSignal, self.passwordVerificationField.rac_textSignal ] reduce:^(NSString *username, NSString *email, NSString *password, NSString *passwordVerification) { return @([username length] > 0 && [email length] > 0 && [password length] > 8 &&

[password isEqual:passwordVerification]); }]; !RAC(self.createButton.enabled) = formValid;

Page 13: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal Cold

• Signals are cold by default__block int aNumber = 0; !// Signal that will have the side effect of incrementing `aNumber` block // variable for each subscription before sending it. RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { aNumber++; [subscriber sendNext:@(aNumber)]; [subscriber sendCompleted]; return nil; }]; !// This will print "subscriber one: 1" [aSignal subscribeNext:^(id x) { NSLog(@"subscriber one: %@", x); }]; !// This will print "subscriber two: 2" [aSignal subscribeNext:^(id x) { NSLog(@"subscriber two: %@", x); }];

Page 14: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal Multicasting

RACSignal *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) { NSMutableArray *filesInProgress = [NSMutableArray array]; for (NSString *path in files) { [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; } ! [subscriber sendNext:[filesInProgress copy]]; [subscriber sendCompleted]; }];

• Multiple shared subscriptions

Page 15: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal Hot

• Multicasted signals are hot, and remain hot until all subscriptions are disposed.RACSignal *dataSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {

…. }]; ![dataSignal subscribeNext:^(id x1) { NSLog(@"First %@", x1); }]; ![dataSignal subscribeNext:^(id x2) { NSLog(@"Second %@", x2); }]; !// x1 and x2 values are same, because are those values are shared through hot signal

Page 16: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal Asynchronous

• Async based operations-(RACSignal *)connect { return [RACSignal createSignal:^(id<RACSubscriber> subscriber) { [externalService connectWithSuccess:^void(id response) {

// Connection succeeded, pass nil (or some useful information) along and complete [subscriber sendNext:response]; [subscriber sendCompleted]; } errorOrTimeout:^void() {

// Error occurred, pass it along to the subscriber [subscriber sendError:someNSError]; }]; }]; }

Page 17: Functional Reactive Programming (CocoaHeads Bratislava)

RACSignal Chaining

[[[[client logIn] then:^{ return [client loadCachedMessages]; }] flattenMap:^(NSArray *messages) { return [client fetchMessagesAfterMessage:messages.lastObject]; }] subscribeError:^(NSError *error) { [self presentError:error]; } completed:^{ NSLog(@"Fetched all messages."); }];

[client logInWithSuccess:^{ [client loadCachedMessagesWithSuccess:^(NSArray *messages) { [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) { NSLog(@"Fetched all messages."); } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }];

• Conventional & and RAC based chained independent operations

Page 18: Functional Reactive Programming (CocoaHeads Bratislava)

Pros• Declarative (code will do it, without telling him

how to do it)

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

• KVO wrapper (less boilerplate code)

• Chain-able

• Smaller code (not in all situations)

Page 19: Functional Reactive Programming (CocoaHeads Bratislava)

Cons

• Sometimes harder to read

• Cannot replace delegate pattern entirely

• UITableView dataSource delegate

• Could have performance hit

• e.g filter (magic behind observing signals)

Page 20: Functional Reactive Programming (CocoaHeads Bratislava)

RAC in Swift• ReactiveCocoa 3.0 (pure in swift with obj-c

bridges)

• Macros not allowed (forget RAC, RACObserve)

• Substitution via custom Structs

• Custom operators

• searchTextField.rac_textSignal() ~> RAC(viewModel, "searchText")

Page 21: Functional Reactive Programming (CocoaHeads Bratislava)

RAC in Swift• Signals with subscription

searchTextField.rac_textSignal().subscribeNext { (next:AnyObject!) -> () in if let text = next as? String { println(countElements(text)) } }

• Signals with subscription reusing swift genericssearchTextField.rac_textSignal().subscribeNextAs { (text:String) -> () in println(countElements(text)) }

Page 22: Functional Reactive Programming (CocoaHeads Bratislava)

Want more?

• https://github.com/ReactiveCocoa/ReactiveCocoa

Page 23: Functional Reactive Programming (CocoaHeads Bratislava)

Thx :) Michal Grman

(Lead senior developer, AIT)