Slide 1

Slide 1 text

Introduction to Functional Reactive Programming with ReactiveCocoa Ian Dundas @id github.com/iandundas/reactivecocoa-presentation

Slide 2

Slide 2 text

Coming up.. • Problems of imperative programming • How Functional Reactive programming addresses these issues • ReactiveCocoa, an implementation of FRP

Slide 3

Slide 3 text

[self.overlayYAxisLayoutConstraint setConstant:200];
 [self.tapGestureRecogniser setEnabled:NO]; BOOL isSyncing= NO; 
 BOOL overlayIsShowing= YES;
 BOOL userIsActive=YES;
 
 x= 1 y= 2
 z= x + y; # z is 3
 y= 4; #change value of y 
 print z: "3" # z is still 3

Slide 4

Slide 4 text

–Justin Spahr-Summers, co-author, on NSBrief podcast "ReactiveCocoa comes from the world of Functional Reactive Programming, which is kinda like a paradigm shift on top of a paradigm shift: it’s Functional Programming with Reactive Programming added on top, which makes it intimidating because it’s a big change from what we’re used to"

Slide 5

Slide 5 text

Functional Programming - from 1000 miles high • Distilled down to an “abhorrence of state” • Functions don’t have side-effects • Immutability • Side-effects are useful, however

Slide 6

Slide 6 text

Reactive Programming x= 1 y= 2
 z= x + y # z is 3
 y= 4 # change value of y 
 print z: “5” # z was updated z “reacts” to the changes of x and y

Slide 7

Slide 7 text

Functional Reactive Programming • Just a combination of functional and reactive paradigms • “We model user input as a function that changes over time, abstracting away the idea of mutable state.” • Can now model difficult things like *user input*

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Signals & Subscriptions • Signal can send: • “Next”, one or many times • “Completed”, or “Error”, only once • We can subscribe to receive these values from a Signal, -> we become a Subscriber.

Slide 10

Slide 10 text

So - what is ReactiveCocoa? • ReactiveCocoa (RAC) is an open source library that brings Functional Reactive Programming paradigm to Objective-C. • It was created by Josh Abernathy & Justin Spahr- Summers in the development of GitHub for Mac.

Slide 11

Slide 11 text

Signals in RAC @property(nonatomic, copy) NSString *firstName; RACSignal *firstNameSignal= RACObserve(self, firstName);

Slide 12

Slide 12 text

“Subscribing” to a Signal [RACObserve(self, firstName)
 subscribeNext:^(NSString *newName) {
 NSLog(@"Name updated: %@", newName);
 }
 ]; 


Slide 13

Slide 13 text

Signal Operators: Map: [[RACObserve(self, isLoggedIn) 
 map:^id(NSNumber *boolValue){
 BOOL enabled= [boolValue boolValue];
 return enabled? @"Logged in": @"Logged out";
 }] 
 subscribeNext:^(NSString *loggedInString){
 NSLog(@"We are currently %@", loggedInString);
 // output: "We are currently: Logged in” [self.tableView reloadData]; // side-effect
 }]; @property(nonatomic) BOOL *isLoggedIn;

Slide 14

Slide 14 text

Signal Operators: Filter: [[RACObserve(self, username)
 filter:^(NSString *newName) {
 return newName.length > 4;
 }]
 subscribeNext:^(NSString *newName) {
 NSLog(@"%@", newName);
 }];
 @property (nonatomic, copy) NSString *username;

Slide 15

Slide 15 text

Binding with RAC() RAC(self.firstNameLabel, text) = RACObserve(self, firstName); binds self.firstNameLabel’s text property to the signal produces a signal which sends a new value each time self.firstName changes

Slide 16

Slide 16 text

Signal Operators: combineLatest:reduce:

Slide 17

Slide 17 text

! Powerful Binding ! & example of combineLatest:reduce: RACSignal *loginShouldBeEnabledSignal = [RACSignal 
 combineLatest:@[
 self.usernameTextField.rac_textSignal, 
 self.passwordTextField.rac_textSignal,
 RACObserve(LoginManager.sharedManager, loggingIn)
 ] 
 reduce: (id) ^(NSString *username, NSString *password, NSNumber *loggingIn) {
 BOOL shouldBeEnabled= 
 (username.length > 0 && password.length > 0 && !loggingIn.boolValue);
 
 return @(shouldBeEnabled);
 }
 ];
 // Bind signal to `enabled` property on loginButton
 RAC(self.loginButton, enabled) = loginShouldBeEnabledSignal;

Slide 18

Slide 18 text

–ReactiveCocoa README.md “By chaining, combining, and reacting to signals, software can be written declaratively, without the need for code that continually observes and updates values (manually).”

Slide 19

Slide 19 text

Derived State • A core concept in Functional Reactive Programming • State is OK in ReactiveCocoa, as long as its bound to a signal • You never explicitly set the value of a bound property, but rather rely on signal transformations to derive that state for you. http://reactivecocoa.io/philosophy.html

Slide 20

Slide 20 text

Getting started: 
 Replace your KVO with RAC @implementation IDViewController
 
 -(void)dealloc{
 [self teardownObservers];
 }
 
 -(void)viewDidLoad {
 [super viewDidLoad];
 [self setupObservers];
 }
 
 -(void)setupObservers{
 [self.viewModel addObserver:self
 forKeyPath:@"firstName"
 options:NSKeyValueObservingOptionNew context:NULL
 ];
 }
 
 -(void)teardownObservers{
 [self.viewModel removeObserver:self forKeyPath:@"firstName"];
 }
 
 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
 change:(NSDictionary *)change context:(void *)context{
 
 if ([keyPath isEqualToString:@"firstName"]){
 NSLog(@"firstName did change");
 }
 }
 @end

Slide 21

Slide 21 text

Getting started: 
 Replace your KVO with RAC @implementation IDViewController
 
 -(void)viewDidLoad {
 [super viewDidLoad];
 
 [RACObserve(self, firstName) subscribeNext:^(id x) {
 NSLog(@"firstName did change");
 }];
 }
 
 @end

Slide 22

Slide 22 text

Throttling Cool things to do #1
 (which are otherwise tricky)

Slide 23

Slide 23 text

TakeUntil Cool things to do #2
 (which are otherwise tricky)

Slide 24

Slide 24 text

DeliverOn Cool things to do #3
 (which are otherwise tricky)

Slide 25

Slide 25 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 26

Slide 26 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }] 
 http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 27

Slide 27 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }]
 throttle:0.5] http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 28

Slide 28 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }]
 throttle:0.5]
 flattenMap:^RACStream * (NSString *text) {
 return [self signalForSearchWithText:text];
 }]
 http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 29

Slide 29 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }]
 throttle:0.5]
 flattenMap:^RACStream * (NSString *text) {
 return [self signalForSearchWithText:text];
 }]
 map:^id (NSDictionary *jsonSearchResult) {
 NSArray *statuses = jsonSearchResult[@"statuses"];
 return statuses;
 }]
 http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 30

Slide 30 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }]
 throttle:0.5]
 flattenMap:^RACStream * (NSString *text) {
 return [self signalForSearchWithText:text];
 }]
 map:^id (NSDictionary *jsonSearchResult) {
 NSArray *statuses = jsonSearchResult[@"statuses"];
 return statuses;
 }]
 deliverOn:[RACScheduler mainThreadScheduler]]
 http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 31

Slide 31 text

[[[[[[[[self requestAccessToTwitterSignal]
 then:^RACSignal * {
 return self.searchTextField.rac_textSignal;
 }]
 filter:^BOOL (NSString *text) {
 return [self isValidSearchText:text];
 }]
 throttle:0.5]
 flattenMap:^RACStream * (NSString *text) {
 return [self signalForSearchWithText:text];
 }]
 map:^id (NSDictionary *jsonSearchResult) {
 NSArray *statuses = jsonSearchResult[@"statuses"];
 return statuses;
 }]
 deliverOn:[RACScheduler mainThreadScheduler]]
 subscribeNext:^(NSArray *tweets) {
 [self displayTweets:tweets];
 } error:^(NSError *error) {
 NSLog(@"An error occurred: %@", error);
 }]; http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2

Slide 32

Slide 32 text

–Evan Coleman, http://is.gd/kTQhF2 “At first, none of it made any sense to me. I spent the next week pouring over the ReactiveCocoa docs, but no matter how much I read, signals, etc .. they were all just words to me...It wasn’t until I dove head first into the code that I started to see the light."

Slide 33

Slide 33 text

–Ian Dundas, 2015 “Try it”