SlideShare a Scribd company logo
ReactiveCocoa
in Practice
Jeames Bone Mark Corbyn
@outware
www.outware.com.au
@jeamesbone @markcorbyn
ReactiveCocoa
What is ReactiveCocoa?
Functional Reactive Programming (FRP) framework
for iOS and OSX applications.
Why ReactiveCocoa?
“Instead of telling a computer how to do it’s job, why don’t we just tell it
what it’s job is, and let it figure the rest out.”
ReactiveCocoa in a Nutshell
The main component in ReactiveCocoa is the Signal.
Signals represent streams of values over time.
1 3 2 T
Signals
h he hel T
[textField.rac_textSignal subscribeNext:^(NSString *text) {
NSLog(@"text: ‘%@’", text);
}];
> text: ‘h’
> text: ‘he’
> text: ‘hel’
Operators
1 3 2
[signalA map:^(NSNumber *num) {
return num * 10;
}];
10 30 20
Operators
1 3 2
[signalA filter:^(NSNumber *num) {
return num < 3;
}];
1 2
Operators
a b c
1 3 2
[signalA merge:signalB]
1 3 2a b c
What’s Next?
?
1. Reactive View Controller
2. Reactive Notifications
3. Functional Data
Processing
Something Simple
Authentication
/// Stores authentication credentials and tells us if we're authenticated.
@protocol AuthStore <NSObject>
@property (nonatomic, readonly) BOOL authenticated;
- (void)storeAccessCredentials:(AccessCredentials *)accessCredentials;
- (nullable AccessCredentials *)retrieveAccessCredentials;
- (void)removeAccessCredentials;
@end
Observe Authentication Changes
RACSignal *auth = RACObserve(authStore, authenticated);
YES NO YES
Select the Right View Controller
RACSignal *authSignal = RACObserve(authStore, authenticated)
// Pick a view controller
RACSignal *viewControllerSignal =
[authenticatedSignal map:^(NSNumber *isAuthenticated) {
if (isAuthenticated.boolValue)
return [DashboardViewController new];
} else {
return [LoginViewController new];
}
}];
Select the Right View Controller
YES NO YES
Dash
board
Login
Dash
board
map { // BOOL to ViewController }
Displaying it on Screen
How do we get the value out?
We have to subscribe, like a callback.
- (void)viewDidLoad {
[super viewDidLoad];
// Whenever we get a new view controller, PUSH IT
[[viewControllerSignal deliverOnMainThread]
subscribeNext:^(UIViewController *viewController) {
[self showViewController:viewController sender:self];
}];
}
Ok, maybe that’s pushing it
Let’s make a custom view controller
container!
Switching to the latest view controller
@interface SwitchingController : UIViewController
- (instancetype)initWithViewControllers:(RACSignal *)viewControllers;
@property (nonatomic, readonly) UIViewController *currentViewController;
@end
Manages a signal of view controllers, always
displaying the most recent one
- (void)viewDidLoad {
[super viewDidLoad];
[[self.viewControllers deliverOnMainThread]
subscribeNext:^(UIViewController *viewController) {
[self transitionFrom:self.currentViewController to:viewController];
}];
}
Setting Up
When the view loads, subscribe to our signal
- (void)transitionFrom:(UIViewController *)fromViewController
to:(UIViewController *)toViewController {
if (!fromViewController) {
[self addInitialViewController:toViewController];
return;
}
[self addChildViewController:nextViewController];
nextViewController.view.frame = self.view.bounds;
[self.view addSubview:nextViewController.view];
[previousViewController willMoveToParentViewController:nil];
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:nil
completion:^(BOOL finished) {
[toViewController didMoveToParentViewController:self];
[fromViewController removeFromParentViewController];
self.currentViewController = toViewController;
}];
}
Transitioning
YES NO YES
DashboardLogin
Finishing Up
What happens if the view controller
changes rapidly?
- (void)viewDidLoad {
[super viewDidLoad];
[[[self.viewControllers
throttle:0.5]
deliverOnMainThread]
subscribeNext:^(UIViewController *viewController) {
[self transitionFrom:self.currentViewController
to:viewController];
}];
}
Simple Throttling
Only take a new view controller if 0.5 seconds have
passed.
v2: Only throttle while animating
Keep track of when we’re animating
- (void)transitionFrom:(UIViewController *)fromViewController
to:(UIViewController *)toViewController {
// code
self.animating = YES;
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:nil
completion:^(BOOL finished) {
// more code
self.animating = NO;
}];
}
v2: Only throttle while animating
Throttle only if we are animating
[[[self.viewControllers
throttle:0.5 valuesPassingTest:^(id _) {
return self.isAnimating;
}]
deliverOnMainThread]
subscribeNext:^(UIViewController *viewController) {
[self transitionFrom:self.currentViewController to:viewController];
}];
What have we learned?
• Signals can represent real world events (Logging in/out)
• We can transform them using operators like map
• We can control timing through operators like throttle
1. Reactive View Controller
2. Reactive Notifications
3. Functional Data
Processing
Hot Signals
• Events happen regardless of any observers.
• Stream of *events* happening in the world.
• e.g. UI interaction, notifications
Cold Signals
• Subscribing starts the stream of events.
• Stream of results caused by some side effects.
• e.g. network calls, database transactions
Push Notifications
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
// Do something horrible™ in here
}
A Better Option
typedef NS_ENUM(NSUInteger, NotificationType) {
NotificationTypeA,
NotificationTypeB,
NotificationTypeC,
};
@protocol NotificationProvider
- (RACSignal *)notificationSignalForNotificationType:
(NotificationType)type;
@end
We Want This:
@property id<NotificationProvider> notificationProvider;
- (void)viewDidLoad {
[[[self.notificationProvider
notificationSignalForNotificationType:NotificationTypeA]
deliverOnMainThread]
subscribeNext:^(Notification *notification) {
[self updateInterfaceWithNotification:notification];
}]
}
A New Friend!
Lift a selector into the reactive world
- (RACSignal *)rac_signalForSelector:(SEL)selector;
The returned signal will fire an event every time the
method is called.
Let’s Do It!
- (RACSignal *)notificationSignalForNotificationType:
(NotificationType)type {
return [[[self
rac_signalForSelector:@selector(application:didReceiveRemoteNotification:)]
map:^(RACTuple *arguments) {
return arguments.second;
}]
map:^(NSDictionary *userInfo) {
// Parse our user info dictionary into a model object
return [self parseNotification:userInfo];
}]
filter:^(Notification *notification)
notification.type = type;
}];
}
NSDict NSDict NSDict
Notificati
on
A
Notificati
on
B
Notificati
on
A
map { [self parseNotification:userInfo]; }
filter { notification.type == typeA }
Notificati
on
A
Notificati
on
A
A Wild Local Notification Appears!
• We don't want to duplicate our current notification handling.
• Local and remote notifications should have the same effects.
RACSignal *remoteNotificationInfo = [[self
rac_signalForSelector:@selector(application:didReceiveRemoteNotification:)]
map:^(RACTuple *arguments) {
return arguments.second
}];
RACSignal *localNotificationInfo = [[self
rac_signalForSelector:@selector(application:didReceiveLocalNotification:)]
map:^(RACTuple *arguments) {
UILocalNotification *notification = arguments.second;
return notification.userInfo;
}];
return [[[remoteNotificationInfo merge:localNotificationInfo]
map:^(NSDictionary *userInfo) {
// Parse our user info dictionary into a model object
return [self parseNotification:userInfo];
}]
filter:^(Notification *notification) {
return notification.type == type;
}];
Problem
• We only get notifications sent *after* we subscribe.
• We can't easily update app state or UI that is created after

the notification is sent.
Solution?
[signal replayLast]
Whenever you subscribe to the signal, it will immediately
send you the most recent value from the stream.
Replay it
return [[[remoteNotificationInfo merge:localNotificationInfo]
map:^(NSDictionary *userInfo) {
// Parse our user info dictionary into a model object
return [self parseNotification:userInfo];
}]
filter:^(Notification *notification) {
return notification.type == type;
}]
replayLast];
return [[remoteNotificationInfo merge:localNotificationInfo]
map:^(NSDictionary *userInfo) {
// Parse our user info dictionary into a model object
return [self parseNotification:userInfo];
}]
filter:^(Notification *notification) {
return notification.type == type;
}];
What have we learned?
• Signals can model complex app behaviour like notifications
• We can combine signals in interesting ways
• Helpers like signalForSelector allow us to lift regular functions into signals
1. Reactive View Controller
2. Reactive Notifications
3. Functional Data
Processing
Describing an Algorithm with Functions
Example: Finding the best voucher to cover a
purchase
• If there are any vouchers with higher value than the purchase,
use the lowest valued of those
• Otherwise, use the highest valued voucher available
Imperative Approach
NSArray *sortedVouchers = [vouchers
sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1,
id<Voucher> voucher2) {
return [voucher1 compare:voucher2];
}];
NSArray *vouchers = [[self voucherLibrary] vouchers];
id<Voucher> bestVoucher = nil;
for (id<Voucher> voucher in sortedVouchers) {
NSDecimalNumber *voucherAmount = voucher.amount;
if (!voucherAmount) continue;
if ([voucherAmount isLessThan:purchaseAmount]) {
bestVoucher = voucher;
} else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) {
bestVoucher = voucher;
break;
}
}
return bestVoucher;
}
NSMutableArray *vouchersWithValue = [NSMutableArray array];
for (id<Voucher> voucher in sortedVouchers) {
if (voucher.amount) {
[vouchersWithValue addObject:voucher];
}
}
id<Voucher> bestVoucher = nil;
for (id<Voucher> voucher in vouchersWithValue) {
NSDecimalNumber *voucherAmount = voucher.amount;
if ([voucherAmount isLessThen:purchaseAmount]) {
bestVoucher = voucher;
} else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) {
bestVoucher = voucher;
break;
}
}
Separate the Filter?
Functional Approach
- (id<Voucher>)voucherForPurchaseAmount:(NSDecimalNumber *)purchaseAmount {
[[[[[[[[self voucherLibrary]
vouchers]
sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1,
id<Voucher> voucher2) {
return [voucher1 compare:voucher2];
}]
rac_sequence]
filter:^BOOL(id<Voucher> voucher) {
return voucher.amount != nil;
}]
yow_takeUptoBlock:^BOOL(id<Voucher> voucher) {
return [voucher.amount isGreaterThanOrEqualTo:purchaseAmount];
}]
array]
lastObject];
}
NSArray *sortedVouchers = [vouchers
sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1,
id<Voucher> voucher2) {
return [voucher1 compare:voucher2];
}];
NSArray *vouchers = [[self voucherLibrary] vouchers];
id<Voucher> bestVoucher = nil;
for (id<Voucher> voucher in sortedVouchers) {
NSDecimalNumber *voucherAmount = voucher.amount;
if (!voucherAmount) continue;
if ([voucherAmount isLessThan:purchaseAmount]) {
bestVoucher = voucher;
} else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) {
bestVoucher = voucher;
break;
}
}
return bestVoucher;
}
What have we learned?
• Functional code can be more readable
• It's easy to convert between imperative and functional
code using ReactiveCocoa
• Signals are lazy
1. Reactive View Controller
2. Reactive Notifications
3. Functional Data
Processing
Resources
• reactivecocoa.io
• ReactiveCocoa on GitHub
• ReactiveCocoa - The Definitive Introduction (Ray Wenderlich)
• A First Look at ReactiveCocoa 3.0 (Scott Logic)
Next Steps
• Try it out!
• Don’t be scared to go all in
Thanks!
Questions?

More Related Content

What's hot

Angular and The Case for RxJS
Angular and The Case for RxJSAngular and The Case for RxJS
Angular and The Case for RxJS
Sandi Barr
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
Brainhub
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
Mario Fusco
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
Christoffer Noring
 
Reactive cocoa
Reactive cocoaReactive cocoa
Reactive cocoa
iacisclo
 
Oop assignment 02
Oop assignment 02Oop assignment 02
Oop assignment 02
MamoonKhan39
 
My Gentle Introduction to RxJS
My Gentle Introduction to RxJSMy Gentle Introduction to RxJS
My Gentle Introduction to RxJS
Mattia Occhiuto
 
Reactive Java (33rd Degree)
Reactive Java (33rd Degree)Reactive Java (33rd Degree)
Reactive Java (33rd Degree)
Tomasz Kowalczewski
 
Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
Christoffer Noring
 
Cocoa heads 09112017
Cocoa heads 09112017Cocoa heads 09112017
Cocoa heads 09112017
Vincent Pradeilles
 
Functional Reactive Programming (FRP): Working with RxJS
Functional Reactive Programming (FRP): Working with RxJSFunctional Reactive Programming (FRP): Working with RxJS
Functional Reactive Programming (FRP): Working with RxJS
Oswald Campesato
 
Monads in Swift
Monads in SwiftMonads in Swift
Monads in Swift
Vincent Pradeilles
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
Christoffer Noring
 
Reactive programming with RxJava
Reactive programming with RxJavaReactive programming with RxJava
Reactive programming with RxJava
Jobaer Chowdhury
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
cacois
 
RxJava on Android
RxJava on AndroidRxJava on Android
RxJava on Android
Dustin Graham
 
Node Boot Camp
Node Boot CampNode Boot Camp
Node Boot Camp
Troy Miles
 
Advanced functional programing in Swift
Advanced functional programing in SwiftAdvanced functional programing in Swift
Advanced functional programing in Swift
Vincent Pradeilles
 

What's hot (20)

Angular and The Case for RxJS
Angular and The Case for RxJSAngular and The Case for RxJS
Angular and The Case for RxJS
 
Rxjs ppt
Rxjs pptRxjs ppt
Rxjs ppt
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
Reactive cocoa
Reactive cocoaReactive cocoa
Reactive cocoa
 
Oop assignment 02
Oop assignment 02Oop assignment 02
Oop assignment 02
 
My Gentle Introduction to RxJS
My Gentle Introduction to RxJSMy Gentle Introduction to RxJS
My Gentle Introduction to RxJS
 
Reactive Java (33rd Degree)
Reactive Java (33rd Degree)Reactive Java (33rd Degree)
Reactive Java (33rd Degree)
 
Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
 
Map kit light
Map kit lightMap kit light
Map kit light
 
Cocoa heads 09112017
Cocoa heads 09112017Cocoa heads 09112017
Cocoa heads 09112017
 
Functional Reactive Programming (FRP): Working with RxJS
Functional Reactive Programming (FRP): Working with RxJSFunctional Reactive Programming (FRP): Working with RxJS
Functional Reactive Programming (FRP): Working with RxJS
 
Monads in Swift
Monads in SwiftMonads in Swift
Monads in Swift
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
 
Reactive programming with RxJava
Reactive programming with RxJavaReactive programming with RxJava
Reactive programming with RxJava
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
RxJava on Android
RxJava on AndroidRxJava on Android
RxJava on Android
 
Node Boot Camp
Node Boot CampNode Boot Camp
Node Boot Camp
 
Advanced functional programing in Swift
Advanced functional programing in SwiftAdvanced functional programing in Swift
Advanced functional programing in Swift
 

Similar to ReactiveCocoa in Practice

Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOS
Make School
 
iOS Multithreading
iOS MultithreadingiOS Multithreading
iOS MultithreadingRicha Jain
 
Angular 16 – the rise of Signals
Angular 16 – the rise of SignalsAngular 16 – the rise of Signals
Angular 16 – the rise of Signals
Coding Academy
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of Control
Chad Hietala
 
Windows phone 7 series
Windows phone 7 seriesWindows phone 7 series
Windows phone 7 seriesopenbala
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonf
Nataliya Patsovska
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
Visual Engineering
 
Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Alessandro Molina
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code
jonmarimba
 
Ngrx meta reducers
Ngrx meta reducersNgrx meta reducers
Ngrx meta reducers
Eliran Eliassy
 
Saving lives with rx java
Saving lives with rx javaSaving lives with rx java
Saving lives with rx java
Shahar Barsheshet
 
JSAnkara Swift v React Native
JSAnkara Swift v React NativeJSAnkara Swift v React Native
JSAnkara Swift v React Native
Muhammed Demirci
 
A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015
Matthias Noback
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
BOSC Tech Labs
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Codemotion
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Mario Fusco
 
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. LaukePointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Patrick Lauke
 
Introduction to VIPER Architecture
Introduction to VIPER ArchitectureIntroduction to VIPER Architecture
Introduction to VIPER Architecture
Hendy Christianto
 
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular CodebasesITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
Istanbul Tech Talks
 
Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJava
Rick Warren
 

Similar to ReactiveCocoa in Practice (20)

Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOS
 
iOS Multithreading
iOS MultithreadingiOS Multithreading
iOS Multithreading
 
Angular 16 – the rise of Signals
Angular 16 – the rise of SignalsAngular 16 – the rise of Signals
Angular 16 – the rise of Signals
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of Control
 
Windows phone 7 series
Windows phone 7 seriesWindows phone 7 series
Windows phone 7 series
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonf
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2Reactive & Realtime Web Applications with TurboGears2
Reactive & Realtime Web Applications with TurboGears2
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code
 
Ngrx meta reducers
Ngrx meta reducersNgrx meta reducers
Ngrx meta reducers
 
Saving lives with rx java
Saving lives with rx javaSaving lives with rx java
Saving lives with rx java
 
JSAnkara Swift v React Native
JSAnkara Swift v React NativeJSAnkara Swift v React Native
JSAnkara Swift v React Native
 
A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. LaukePointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
Pointer Events Working Group update / TPAC 2023 / Patrick H. Lauke
 
Introduction to VIPER Architecture
Introduction to VIPER ArchitectureIntroduction to VIPER Architecture
Introduction to VIPER Architecture
 
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular CodebasesITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
 
Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJava
 

ReactiveCocoa in Practice

  • 2. Jeames Bone Mark Corbyn @outware www.outware.com.au @jeamesbone @markcorbyn
  • 4. What is ReactiveCocoa? Functional Reactive Programming (FRP) framework for iOS and OSX applications.
  • 5. Why ReactiveCocoa? “Instead of telling a computer how to do it’s job, why don’t we just tell it what it’s job is, and let it figure the rest out.”
  • 6. ReactiveCocoa in a Nutshell The main component in ReactiveCocoa is the Signal. Signals represent streams of values over time. 1 3 2 T
  • 7. Signals h he hel T [textField.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"text: ‘%@’", text); }]; > text: ‘h’ > text: ‘he’ > text: ‘hel’
  • 8. Operators 1 3 2 [signalA map:^(NSNumber *num) { return num * 10; }]; 10 30 20
  • 9. Operators 1 3 2 [signalA filter:^(NSNumber *num) { return num < 3; }]; 1 2
  • 10. Operators a b c 1 3 2 [signalA merge:signalB] 1 3 2a b c
  • 12. 1. Reactive View Controller 2. Reactive Notifications 3. Functional Data Processing
  • 13. Something Simple Authentication /// Stores authentication credentials and tells us if we're authenticated. @protocol AuthStore <NSObject> @property (nonatomic, readonly) BOOL authenticated; - (void)storeAccessCredentials:(AccessCredentials *)accessCredentials; - (nullable AccessCredentials *)retrieveAccessCredentials; - (void)removeAccessCredentials; @end
  • 14. Observe Authentication Changes RACSignal *auth = RACObserve(authStore, authenticated); YES NO YES
  • 15. Select the Right View Controller RACSignal *authSignal = RACObserve(authStore, authenticated) // Pick a view controller RACSignal *viewControllerSignal = [authenticatedSignal map:^(NSNumber *isAuthenticated) { if (isAuthenticated.boolValue) return [DashboardViewController new]; } else { return [LoginViewController new]; } }];
  • 16. Select the Right View Controller YES NO YES Dash board Login Dash board map { // BOOL to ViewController }
  • 17. Displaying it on Screen How do we get the value out? We have to subscribe, like a callback. - (void)viewDidLoad { [super viewDidLoad]; // Whenever we get a new view controller, PUSH IT [[viewControllerSignal deliverOnMainThread] subscribeNext:^(UIViewController *viewController) { [self showViewController:viewController sender:self]; }]; }
  • 18. Ok, maybe that’s pushing it Let’s make a custom view controller container!
  • 19. Switching to the latest view controller @interface SwitchingController : UIViewController - (instancetype)initWithViewControllers:(RACSignal *)viewControllers; @property (nonatomic, readonly) UIViewController *currentViewController; @end Manages a signal of view controllers, always displaying the most recent one
  • 20. - (void)viewDidLoad { [super viewDidLoad]; [[self.viewControllers deliverOnMainThread] subscribeNext:^(UIViewController *viewController) { [self transitionFrom:self.currentViewController to:viewController]; }]; } Setting Up When the view loads, subscribe to our signal
  • 21. - (void)transitionFrom:(UIViewController *)fromViewController to:(UIViewController *)toViewController { if (!fromViewController) { [self addInitialViewController:toViewController]; return; } [self addChildViewController:nextViewController]; nextViewController.view.frame = self.view.bounds; [self.view addSubview:nextViewController.view]; [previousViewController willMoveToParentViewController:nil]; [self transitionFromViewController:fromViewController toViewController:toViewController duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) { [toViewController didMoveToParentViewController:self]; [fromViewController removeFromParentViewController]; self.currentViewController = toViewController; }]; } Transitioning
  • 23. Finishing Up What happens if the view controller changes rapidly?
  • 24. - (void)viewDidLoad { [super viewDidLoad]; [[[self.viewControllers throttle:0.5] deliverOnMainThread] subscribeNext:^(UIViewController *viewController) { [self transitionFrom:self.currentViewController to:viewController]; }]; } Simple Throttling Only take a new view controller if 0.5 seconds have passed.
  • 25. v2: Only throttle while animating Keep track of when we’re animating - (void)transitionFrom:(UIViewController *)fromViewController to:(UIViewController *)toViewController { // code self.animating = YES; [self transitionFromViewController:fromViewController toViewController:toViewController duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) { // more code self.animating = NO; }]; }
  • 26. v2: Only throttle while animating Throttle only if we are animating [[[self.viewControllers throttle:0.5 valuesPassingTest:^(id _) { return self.isAnimating; }] deliverOnMainThread] subscribeNext:^(UIViewController *viewController) { [self transitionFrom:self.currentViewController to:viewController]; }];
  • 27. What have we learned? • Signals can represent real world events (Logging in/out) • We can transform them using operators like map • We can control timing through operators like throttle
  • 28. 1. Reactive View Controller 2. Reactive Notifications 3. Functional Data Processing
  • 29. Hot Signals • Events happen regardless of any observers. • Stream of *events* happening in the world. • e.g. UI interaction, notifications
  • 30. Cold Signals • Subscribing starts the stream of events. • Stream of results caused by some side effects. • e.g. network calls, database transactions
  • 31. Push Notifications - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { // Do something horrible™ in here }
  • 32. A Better Option typedef NS_ENUM(NSUInteger, NotificationType) { NotificationTypeA, NotificationTypeB, NotificationTypeC, }; @protocol NotificationProvider - (RACSignal *)notificationSignalForNotificationType: (NotificationType)type; @end
  • 33. We Want This: @property id<NotificationProvider> notificationProvider; - (void)viewDidLoad { [[[self.notificationProvider notificationSignalForNotificationType:NotificationTypeA] deliverOnMainThread] subscribeNext:^(Notification *notification) { [self updateInterfaceWithNotification:notification]; }] }
  • 34. A New Friend! Lift a selector into the reactive world - (RACSignal *)rac_signalForSelector:(SEL)selector; The returned signal will fire an event every time the method is called.
  • 35. Let’s Do It! - (RACSignal *)notificationSignalForNotificationType: (NotificationType)type { return [[[self rac_signalForSelector:@selector(application:didReceiveRemoteNotification:)] map:^(RACTuple *arguments) { return arguments.second; }] map:^(NSDictionary *userInfo) { // Parse our user info dictionary into a model object return [self parseNotification:userInfo]; }] filter:^(Notification *notification) notification.type = type; }]; }
  • 36. NSDict NSDict NSDict Notificati on A Notificati on B Notificati on A map { [self parseNotification:userInfo]; } filter { notification.type == typeA } Notificati on A Notificati on A
  • 37. A Wild Local Notification Appears! • We don't want to duplicate our current notification handling. • Local and remote notifications should have the same effects.
  • 38. RACSignal *remoteNotificationInfo = [[self rac_signalForSelector:@selector(application:didReceiveRemoteNotification:)] map:^(RACTuple *arguments) { return arguments.second }]; RACSignal *localNotificationInfo = [[self rac_signalForSelector:@selector(application:didReceiveLocalNotification:)] map:^(RACTuple *arguments) { UILocalNotification *notification = arguments.second; return notification.userInfo; }]; return [[[remoteNotificationInfo merge:localNotificationInfo] map:^(NSDictionary *userInfo) { // Parse our user info dictionary into a model object return [self parseNotification:userInfo]; }] filter:^(Notification *notification) { return notification.type == type; }];
  • 39. Problem • We only get notifications sent *after* we subscribe. • We can't easily update app state or UI that is created after
 the notification is sent.
  • 40. Solution? [signal replayLast] Whenever you subscribe to the signal, it will immediately send you the most recent value from the stream.
  • 41. Replay it return [[[remoteNotificationInfo merge:localNotificationInfo] map:^(NSDictionary *userInfo) { // Parse our user info dictionary into a model object return [self parseNotification:userInfo]; }] filter:^(Notification *notification) { return notification.type == type; }] replayLast]; return [[remoteNotificationInfo merge:localNotificationInfo] map:^(NSDictionary *userInfo) { // Parse our user info dictionary into a model object return [self parseNotification:userInfo]; }] filter:^(Notification *notification) { return notification.type == type; }];
  • 42. What have we learned? • Signals can model complex app behaviour like notifications • We can combine signals in interesting ways • Helpers like signalForSelector allow us to lift regular functions into signals
  • 43. 1. Reactive View Controller 2. Reactive Notifications 3. Functional Data Processing
  • 44. Describing an Algorithm with Functions Example: Finding the best voucher to cover a purchase • If there are any vouchers with higher value than the purchase, use the lowest valued of those • Otherwise, use the highest valued voucher available
  • 46. NSArray *sortedVouchers = [vouchers sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1, id<Voucher> voucher2) { return [voucher1 compare:voucher2]; }]; NSArray *vouchers = [[self voucherLibrary] vouchers]; id<Voucher> bestVoucher = nil; for (id<Voucher> voucher in sortedVouchers) { NSDecimalNumber *voucherAmount = voucher.amount; if (!voucherAmount) continue; if ([voucherAmount isLessThan:purchaseAmount]) { bestVoucher = voucher; } else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) { bestVoucher = voucher; break; } } return bestVoucher; }
  • 47. NSMutableArray *vouchersWithValue = [NSMutableArray array]; for (id<Voucher> voucher in sortedVouchers) { if (voucher.amount) { [vouchersWithValue addObject:voucher]; } } id<Voucher> bestVoucher = nil; for (id<Voucher> voucher in vouchersWithValue) { NSDecimalNumber *voucherAmount = voucher.amount; if ([voucherAmount isLessThen:purchaseAmount]) { bestVoucher = voucher; } else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) { bestVoucher = voucher; break; } } Separate the Filter?
  • 49. - (id<Voucher>)voucherForPurchaseAmount:(NSDecimalNumber *)purchaseAmount { [[[[[[[[self voucherLibrary] vouchers] sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1, id<Voucher> voucher2) { return [voucher1 compare:voucher2]; }] rac_sequence] filter:^BOOL(id<Voucher> voucher) { return voucher.amount != nil; }] yow_takeUptoBlock:^BOOL(id<Voucher> voucher) { return [voucher.amount isGreaterThanOrEqualTo:purchaseAmount]; }] array] lastObject]; }
  • 50. NSArray *sortedVouchers = [vouchers sortedArrayUsingComparator:^NSComparisonResult(id<Voucher> voucher1, id<Voucher> voucher2) { return [voucher1 compare:voucher2]; }]; NSArray *vouchers = [[self voucherLibrary] vouchers]; id<Voucher> bestVoucher = nil; for (id<Voucher> voucher in sortedVouchers) { NSDecimalNumber *voucherAmount = voucher.amount; if (!voucherAmount) continue; if ([voucherAmount isLessThan:purchaseAmount]) { bestVoucher = voucher; } else if ([voucherAmount isGreaterThanOrEqualTo:purchaseAmount]) { bestVoucher = voucher; break; } } return bestVoucher; }
  • 51. What have we learned? • Functional code can be more readable • It's easy to convert between imperative and functional code using ReactiveCocoa • Signals are lazy
  • 52. 1. Reactive View Controller 2. Reactive Notifications 3. Functional Data Processing
  • 53. Resources • reactivecocoa.io • ReactiveCocoa on GitHub • ReactiveCocoa - The Definitive Introduction (Ray Wenderlich) • A First Look at ReactiveCocoa 3.0 (Scott Logic)
  • 54. Next Steps • Try it out! • Don’t be scared to go all in