Lviv MD Day 2015 Павло Захаров "Reactive cocoa: paradigm shift"

Preview:

Citation preview

Pavlo Zakharov

Reactive Cocoa: Paradigm shift.

- What is Reactive functional Paradigm?- Why consider using ReactiveCocoa?- RAC Signal.Unifying all of Cocoa’s common patterns.- RAC and concurrency.- Implementing MVVM design pattern on iOS with RAC.

What is Reactive Functional Programming?

Functional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous dataflow programming) using

the building blocks of functional programming (e.g. map, reduce, filter). FRP has been used for programming graphical user interfaces (GUIs), robotics, and music, aiming to simplify these problems by explicitly

modelling time. (c) Wikipedia

Reactive+Functional:

❖ Static/Dynamic data flow.(Programming based on Events)

❖ (Functional Blocks) Functions passed as arguments to functions.

Cocoa Events

❖ Delegation❖ KVO❖ Target - Action❖ Callback Blocks❖ NSNotifications}RacSignal

RACSignal

❖ Next❖ Error❖ Completed

Ray Signal emits:

[self.searchText.rac_textSignal subscribeNext:^(id x) { NSLog(@"Next ocured"); }error:^(NSError *error) { NSLog(@"Error ocured"); }completed:^{ NSLog(@"Completed"); }];

Signal can have one or multiple subscribers

EventsEvent flow:

CompletedCompleted

NextNext

NextNext

NextNext ErrorError

NextNext NextNext …

Reactive Operations

❖ Map❖ Throttle❖ Skip❖ Filter❖ DeliverOn❖ more…

Operations don’t care about signal source.

Map

❖ Transforms the value of the event(As transformed value as input signal can be literally anything)

RACSignal *mySignal = [textSignal map:^id(NSString *text) { return @(text.length>3); }];[mySignal subscribeNext:^(NSNumber* x) { if ([x boolValue]) { //... } }];

Combine

❖ Combines two RAC Events(Next Emits when one of combined events occurs).

RACSignal *switch1Signal = … RACSignal *switch2Signal = …

RACSignal *both = [RACSignal combineLatest:@[switch1Signal,switch2Signal] reduce:^id(NSNumber *switch1, NSNumber *switch2){ return @([switch1 boolValue] && [switch2 boolValue]); }]; [both subscribeNext:^(id x) { if([x boolValue]){ NSLog(@"Both"); } }];

Filter

❖ Event will be emitted only if the condition is satisfied.

[[self.usernameTextField.rac_textSignal filter:^BOOL(id value) { return (((NSString*)value).length>3); }]subscribeNext:^(id x) { NSLog(@"do stuff"); }];

Throttle

❖ If event occurrence once more within time interval specified event wouldn’t be emitted.(Extremely useful for locking buttons to prevent speed clicking)

[[[self.usernameTextField.rac_textSignal filter:^BOOL(id value) { return (((NSString*)value).length>3); }]throttle:0.5]subscribeNext:^(id x) { NSLog(@"do stuff"); }];

Deliver On

❖ The block will be executed on thread specified (For example for UI stuff on main thread)

[[[self.usernameTextField.rac_textSignal filter:^BOOL(id value) { return (((NSString*)value).length>3); }] deliverOn:[RACScheduler mainThreadScheduler]]subscribeNext:^(id x) { NSLog(@"do UI stuff"); }];

How to mix reactive code with non-reactive?

Option one: doNext block

❖ doNext Block will be executed before subscriberBlock will be called.This is the good place for reactive code.

[[[self.signInButton rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) { //put your non-reactive code here }] subscribeNext:^(NSNumber *signedIn) { //do stuff }];}

Wrap non-reactive code to reactive

❖ Method will create signal which can emit next/error/completed event based on your non-reactive conditions.

-(RACSignal *)switchSignal{ return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { (self.switch1.isOn)?[subscriber sendNext:@(YES)] : [subscriber sendNext:@(NO)]; return nil; }];} //. //. //. [[self switchSignal]subscribeNext:^(id x) { //doStuff }];

Bind Signal to UIEvent

❖ Every time touchUpInside event occurs next event will be emitted;

RACSignal *mySignal = [self.getButton rac_signalForControlEvents:UIControlEventTouchUpInside]

Avoid Retain cycles

❖ Accessing self in block can cause retain cycle and memory leak.Use __weak self to prevent it.

@weakify(self); [self.searchText.rac_textSignal subscribeNext:^(id x) { @strongify(self); // self.smth = smth }error:^(NSError *error) { @strongify(self); // self.smthElse = smth }completed:^{ @strongify(self); // self.smthReallyDifferent = smth }];

ModelViewViewModel

ViewModel

❖ ViewModel which is a special type of model that represents the UI state of the application

ViewView ViewModeViewModell ModelModel

ViewView ViewModeViewModell ModelModel

ControllControllerer

Binding

❖ Binds textField.text to the property in ViewModel. Every time property changes text will change as well. (WPF implementation is shown on the right)

RAC(self.searchText,text) = self.myString;

 <TextBlock TextContent=”{Binding Path=Description}” />~

Why should i use RAC?

❖ Unified interface for all Cocoa events.❖ Less code in comparison with non-reactive code.❖ No massive ViewControllers with complicated

relationships.❖ Easy work with concurrency.❖ No complicated “States” needed.

Some Disadvantages❖ Debug Hell.❖ Little overhead❖ Weird syntax.(But you will get used to it)

Resources

❖ https://github.com/ReactiveCocoa/ReactiveCocoa

❖ http://www.raywenderlich.com❖ http://blog.scottlogic.com/2014/07/24/mvvm-rea

ctivecocoa-swift.html❖ https://www.youtube.com/watch?v=DgdISd1Qc4

8❖ https://www.youtube.com/watch?v=fWV7xyN5C

R8❖ https://github.com/ZakharovPaul/RacDemo.git

Questions?

Thank you Skype: c1ark.E-mail: zakharov.paul@live.com