Functional reactive programing - what is it and what you would use it for?
ReactiveCocoa
- reactive programing in Objective-C
- how it works within Objective-C
- advantages and disadvantages of its use
- reactive programing code samples
- usage within Swift
3. 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
4. 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
6. ReactiveCocoa
Building blocks (RACStream)
• RACStream (new value flows)
• Subscriptions (subscribe to receive values)
• Transformations (transform values as you want)
• RACStream
• RACSequence
• RACSignal
7. 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)
8. 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
9. • 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
10. 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
13. 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);
}];
15. 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
16. 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];
}];
}];
}
18. 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)
19. Cons
• Sometimes harder to read
• Cannot replace delegate pattern entirely
• UITableView dataSource delegate
• Could have performance hit
• e.g filter (magic behind observing signals)
20. 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")
21. 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 generics
searchTextField.rac_textSignal().subscribeNextAs {
(text:String) -> () in
println(countElements(text))
}