ReactiveCocoa made 
Simple with Swift! 
@ColinEberhardt 
ShinobiControls
Tutorials
What is ReactiveCocoa? 
• ReactiveCocoa is an open-source framework 
created by the GitHub team 
• Inspired by Functional Reactive Programming 
(FRP) 
• Changes the way in which we structure our 
applications 
• Inspired by Microsoft’s ReactiveExtensions (Rx)
Functional Reactive Programming 
• Functional Programming 
• First-class functions, passed as arguments 
to other functions 
• Reactive Programming 
• Focusses on data flow 
• Functional Programming + Reactive 
Programming = FRP
FRP - WTF? 
http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming
In my own words … 
• Every line of code we write is executed in reaction 
to an event 
• But … these events all have different interfaces 
• delegates, target-action, KVO, closures 
• ReactiveCocoa provides a standard interface for all 
events 
• Allows us to define a higher level language 
• Easier to test too!
Learn through play
Code 
Build 
Run 
Explain! 
Repeat
Get Reactive 
let textSignal: RACSignal = 
usernameTextField.rac_textSignal() 
! 
textSignal.subscribeNext { 
(text: AnyObject!) -> Void in 
let textString = text as String 
println(textString) 
}
Simplified with Swift 
func subscribeNextAs<T>(nextClosure:(T) -> ()) -> () { 
self.subscribeNext { 
(next: AnyObject!) -> () in 
let nextAsT = next! as T 
nextClosure(nextAsT) 
} 
} 
textSignal.subscribeNextAs { 
(text: String) -> () in 
println(text) 
}
Signals! 
• A signal emits events 
• next 
• error 
• completed 
• A signal can have none, 
one or more subscribers 
textSignal.subscribeNextAs({ 
(text: String) in 
println(text) 
}, error: { (error) in 
// ... 
}, completed: { 
// ... 
})
Events 
Signals can emit none, one or more next 
events, optionally followed by either an error or 
completed 
COMPLETED 
NEXT NEXT ERROR 
NEXT NEXT NEXT NEXT … 
intervals do not 
have to be 
regular!
Signal all things 
• Network request 
• A single next, followed by a completed 
• Large download 
• Multiple next events, representing partial data, 
followed by completed 
• UI control 
• An infinite stream of next events
Operations 
• ReactiveCocoa allows you to perform 
operations on signals 
• map, filter, skip, take, throttle … 
• These operations are entirely agnostic to the 
source of the signal
filter 
let textSignal: RACSignal = usernameTextField.rac_textSignal() 
! 
let filteredText = textSignal.filterAs { 
(text: NSString) -> Bool in 
return text.length > 3 
} 
! 
filteredText.subscribeNextAs { 
(text: String) in 
println(text) 
} 
A filter is a ‘gate’, filtering-out events which do not 
match the given condition
What exactly are events? 
• What does a next event actually look like? 
• Anything! 
• Signals are an interface for handling 
asynchronous events 
• The event contents is context dependant
map 
let textSignal: RACSignal = usernameTextField.rac_textSignal() 
! 
let textLength = textSignal.mapAs { 
(text: NSString) -> NSNumber in 
return text.length 
} 
! 
textLength.subscribeNextAs { 
(length: NSNumber) in 
println(length) 
} 
Transforms each next event
Creating a pipeline 
let textSignal: RACSignal = 
usernameTextField.rac_textSignal() 
! 
let textLength = textSignal.mapAs { 
(text: NSString) -> NSNumber in 
return text.length 
} 
! 
let filteredText = textLength.filterAs { 
(number: NSNumber) -> Bool in 
return number > 3 
} 
! 
filteredText.subscribeNextAs { 
(length: NSNumber) in 
println(length) 
}
Fluent syntax 
usernameTextField.rac_textSignal() 
.mapAs { 
(text: NSString) -> NSNumber in 
return text.length 
}.filterAs { 
(number: NSNumber) -> Bool in 
return number > 3 
}.subscribeNextAs { 
(length: NSNumber) in 
println(length) 
} 
Operations return RACSignal, allowing method 
chaining
rac_textSignal() 
.mapAs { 
(text: NSString) -> NSNumber in 
return text.length 
}.filterAs { 
(number: NSNumber) -> Bool in 
return number > 3 
}.subscribeNextAs { 
(length: NSNumber) in 
println(length) 
} 
Value->-3- 
rac_textSIgnal- map- 
filter- subscribeNext- 
NSString- NSNumber-usernameTextField.
Valid text fields 
let validUsernameSignal = usernameTextField.rac_textSignal() 
.mapAs { (text: NSString) -> NSNumber in 
return self.isValidUsername(text) 
} 
! 
validUsernameSignal.mapAs { 
(valid: NSNumber) -> UIColor in 
return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() 
}.setKeyPath("backgroundColor", onObject: usernameTextField) 
• Unfortunately we need to ‘box’ bool values. 
• I am sure ReactiveSwift will fix this ;-)
More Swift Magic! 
!! 
RAC(usernameTextField, "backgroundColor") << 
validUsernameSignal.mapAs { 
(valid: NSNumber) -> UIColor in 
return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() 
} 
http://napora.org/a-swift-reaction/
More Sweet Sweet Swift! 
func validToBackground(valid: NSNumber) -> UIColor { 
return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() 
} 
! 
func isValidText(validator:(String) -> Bool)(text: NSString) -> NSNumber { 
return validator(text) 
} 
! 
let validUsernameSignal = usernameTextField.rac_textSignal() 
.mapAs(isValidText(isValidUsername)) 
.distinctUntilChanged() 
! 
RAC(usernameTextField, "backgroundColor") << 
validUsernameSignal.mapAs(validToBackground)
Login button enabled state 
let signUpActiveSignal = RACSignalEx.combineLatestAs( 
[validUsernameSignal, validPasswordSignal]) { 
(validUsername: NSNumber, validPassword: NSNumber) -> NSNumber in 
return validUsername && validPassword 
} 
! 
signUpActiveSignal.subscribeNextAs { 
(active: NSNumber) in 
self.signInButton.enabled = active 
} 
Combines two signals into a new one
The pipeline 
NSString- BOOL- UIColor-password-rac_ 
rac_textSIgnal- map- map- backgroundColor- 
BOOL- 
reduce- subscribeNext- 
NSString- BOOL- UIColor-username-combineLatest: 
textSIgnal- map- map- backgroundColor-
Reactive Button Click 
signInButton.rac_signalForControlEvents(.TouchUpInside) 
.subscribeNext { 
(button) in 
println("clicked") 
} 
Replaces target-action with a signal
Creating Signals 
func signInSignal() -> RACSignal { 
return RACSignal.createSignal { 
(subscriber) -> RACDisposable! in 
println("Sign-in initiated") 
self.signInService.signInWithUsername(self.usernameTextField.text, 
password: self.passwordTextField.text) { 
(success) in 
println("Sign-in completed") 
subscriber.sendNext(success) 
subscriber.sendCompleted() 
} 
return nil 
} 
}
Using the sign-in signal 
signInButton.rac_signalForControlEvents(.TouchUpInside) 
.map { 
(any) -> RACSignal in 
self.signInSignal() 
}.subscribeNext { 
(any) in 
println(any) 
}
Doing it right! 
signInButton.rac_signalForControlEvents(.TouchUpInside) 
.flattenMap { 
(any) -> RACSignal in 
self.signInSignal() 
}.subscribeNext { 
(any) in 
println(any) 
} 
flattenMap subscribes to a signal, returning 
the resultant events
Side-effects 
signInButton.rac_signalForControlEvents(.TouchUpInside) 
.doNext { 
(any) in 
self.signInButton.enabled = false; 
}.flattenMap { 
(any) -> RACSignal in 
self.signInSignal() 
}.subscribeNextAs { 
(success: NSNumber) in 
self.signInButton.enabled = true; 
self.handleSignInResult(success.boolValue) 
} 
Side-effects receive next events, but cannot mutate them
Login pipeline 
doNext1 
signInSignal1 
BOOL1 
UIBu3on1 BOOL1 
rac_signalForControlEvents11 fla3enMap1 subscribeNext1
ReactiveCocoa Made Simple 
• A signal emits events 
• next 
• error 
• completed 
• A signal can have multiple subscribers 
• Signals can emit multiple next events, optionally 
followed by either an error or completed
Random 
cool stuff!
[[[[[[[self requestAccessToTwitterSignal] 
then:^RACSignal *{ 
@strongify(self) 
return self.searchText.rac_textSignal; 
}] 
filter:^BOOL(NSString *text) { 
@strongify(self) 
return [self isValidSearchText:text]; 
}] 
throttle:0.5] 
flattenMap:^RACStream *(NSString *text) { 
@strongify(self) 
return [self signalForSearchWithText:text]; 
}] 
deliverOn:[RACScheduler mainThreadScheduler]] 
subscribeNext:^(NSDictionary *jsonSearchResult) { 
NSArray *statuses = jsonSearchResult[@"statuses"]; 
NSArray *tweets = [statuses linq_select:^id(id tweet) { 
return [RWTweet tweetWithStatus:tweet]; 
}]; 
[self.resultsViewController displayTweets:tweets]; 
} error:^(NSError *error) { 
NSLog(@"An error occurred: %@", error); 
}];
https://github.com/ColinEberhardt/ReactiveSwiftFlickrSearch
// a signal that emits events when visibility changes 
let visibleStateChanged = RACObserve(self, "isVisible").skip(1) 
! 
// filtered into visible and hidden signals 
let visibleSignal = visibleStateChanged.filter { $0.boolValue } 
let hiddenSignal = visibleStateChanged.filter { !$0.boolValue } 
! 
// a signal that emits when an item has been visible for 1 second 
let fetchMetadata = visibleSignal.delay(1).takeUntil(hiddenSignal) 
! 
fetchMetadata.subscribeNext { 
(next: AnyObject!) -> () in 
// fetch data 
}
Resources 
• https://github.com/ReactiveCocoa/ReactiveCocoa 
• Read the Change Logs! 
• My Stuff … 
• http://www.raywenderlich.com/u/ColinEberhardt 
• https://github.com/ColinEberhardt 
• The code from this presentation 
• https://github.com/ColinEberhardt/ 
SwiftReactivePlayground
ReactiveCocoa made 
Simple with Swift! 
@ColinEberhardt 
ShinobiControls

Reactive cocoa made Simple with Swift

  • 1.
    ReactiveCocoa made Simplewith Swift! @ColinEberhardt ShinobiControls
  • 2.
  • 3.
    What is ReactiveCocoa? • ReactiveCocoa is an open-source framework created by the GitHub team • Inspired by Functional Reactive Programming (FRP) • Changes the way in which we structure our applications • Inspired by Microsoft’s ReactiveExtensions (Rx)
  • 4.
    Functional Reactive Programming • Functional Programming • First-class functions, passed as arguments to other functions • Reactive Programming • Focusses on data flow • Functional Programming + Reactive Programming = FRP
  • 5.
    FRP - WTF? http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming
  • 6.
    In my ownwords … • Every line of code we write is executed in reaction to an event • But … these events all have different interfaces • delegates, target-action, KVO, closures • ReactiveCocoa provides a standard interface for all events • Allows us to define a higher level language • Easier to test too!
  • 7.
  • 8.
    Code Build Run Explain! Repeat
  • 9.
    Get Reactive lettextSignal: RACSignal = usernameTextField.rac_textSignal() ! textSignal.subscribeNext { (text: AnyObject!) -> Void in let textString = text as String println(textString) }
  • 10.
    Simplified with Swift func subscribeNextAs<T>(nextClosure:(T) -> ()) -> () { self.subscribeNext { (next: AnyObject!) -> () in let nextAsT = next! as T nextClosure(nextAsT) } } textSignal.subscribeNextAs { (text: String) -> () in println(text) }
  • 11.
    Signals! • Asignal emits events • next • error • completed • A signal can have none, one or more subscribers textSignal.subscribeNextAs({ (text: String) in println(text) }, error: { (error) in // ... }, completed: { // ... })
  • 12.
    Events Signals canemit none, one or more next events, optionally followed by either an error or completed COMPLETED NEXT NEXT ERROR NEXT NEXT NEXT NEXT … intervals do not have to be regular!
  • 13.
    Signal all things • Network request • A single next, followed by a completed • Large download • Multiple next events, representing partial data, followed by completed • UI control • An infinite stream of next events
  • 14.
    Operations • ReactiveCocoaallows you to perform operations on signals • map, filter, skip, take, throttle … • These operations are entirely agnostic to the source of the signal
  • 15.
    filter let textSignal:RACSignal = usernameTextField.rac_textSignal() ! let filteredText = textSignal.filterAs { (text: NSString) -> Bool in return text.length > 3 } ! filteredText.subscribeNextAs { (text: String) in println(text) } A filter is a ‘gate’, filtering-out events which do not match the given condition
  • 16.
    What exactly areevents? • What does a next event actually look like? • Anything! • Signals are an interface for handling asynchronous events • The event contents is context dependant
  • 17.
    map let textSignal:RACSignal = usernameTextField.rac_textSignal() ! let textLength = textSignal.mapAs { (text: NSString) -> NSNumber in return text.length } ! textLength.subscribeNextAs { (length: NSNumber) in println(length) } Transforms each next event
  • 18.
    Creating a pipeline let textSignal: RACSignal = usernameTextField.rac_textSignal() ! let textLength = textSignal.mapAs { (text: NSString) -> NSNumber in return text.length } ! let filteredText = textLength.filterAs { (number: NSNumber) -> Bool in return number > 3 } ! filteredText.subscribeNextAs { (length: NSNumber) in println(length) }
  • 19.
    Fluent syntax usernameTextField.rac_textSignal() .mapAs { (text: NSString) -> NSNumber in return text.length }.filterAs { (number: NSNumber) -> Bool in return number > 3 }.subscribeNextAs { (length: NSNumber) in println(length) } Operations return RACSignal, allowing method chaining
  • 20.
    rac_textSignal() .mapAs { (text: NSString) -> NSNumber in return text.length }.filterAs { (number: NSNumber) -> Bool in return number > 3 }.subscribeNextAs { (length: NSNumber) in println(length) } Value->-3- rac_textSIgnal- map- filter- subscribeNext- NSString- NSNumber-usernameTextField.
  • 21.
    Valid text fields let validUsernameSignal = usernameTextField.rac_textSignal() .mapAs { (text: NSString) -> NSNumber in return self.isValidUsername(text) } ! validUsernameSignal.mapAs { (valid: NSNumber) -> UIColor in return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() }.setKeyPath("backgroundColor", onObject: usernameTextField) • Unfortunately we need to ‘box’ bool values. • I am sure ReactiveSwift will fix this ;-)
  • 22.
    More Swift Magic! !! RAC(usernameTextField, "backgroundColor") << validUsernameSignal.mapAs { (valid: NSNumber) -> UIColor in return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() } http://napora.org/a-swift-reaction/
  • 23.
    More Sweet SweetSwift! func validToBackground(valid: NSNumber) -> UIColor { return valid.boolValue ? UIColor.clearColor() : UIColor.yellowColor() } ! func isValidText(validator:(String) -> Bool)(text: NSString) -> NSNumber { return validator(text) } ! let validUsernameSignal = usernameTextField.rac_textSignal() .mapAs(isValidText(isValidUsername)) .distinctUntilChanged() ! RAC(usernameTextField, "backgroundColor") << validUsernameSignal.mapAs(validToBackground)
  • 26.
    Login button enabledstate let signUpActiveSignal = RACSignalEx.combineLatestAs( [validUsernameSignal, validPasswordSignal]) { (validUsername: NSNumber, validPassword: NSNumber) -> NSNumber in return validUsername && validPassword } ! signUpActiveSignal.subscribeNextAs { (active: NSNumber) in self.signInButton.enabled = active } Combines two signals into a new one
  • 27.
    The pipeline NSString-BOOL- UIColor-password-rac_ rac_textSIgnal- map- map- backgroundColor- BOOL- reduce- subscribeNext- NSString- BOOL- UIColor-username-combineLatest: textSIgnal- map- map- backgroundColor-
  • 28.
    Reactive Button Click signInButton.rac_signalForControlEvents(.TouchUpInside) .subscribeNext { (button) in println("clicked") } Replaces target-action with a signal
  • 29.
    Creating Signals funcsignInSignal() -> RACSignal { return RACSignal.createSignal { (subscriber) -> RACDisposable! in println("Sign-in initiated") self.signInService.signInWithUsername(self.usernameTextField.text, password: self.passwordTextField.text) { (success) in println("Sign-in completed") subscriber.sendNext(success) subscriber.sendCompleted() } return nil } }
  • 30.
    Using the sign-insignal signInButton.rac_signalForControlEvents(.TouchUpInside) .map { (any) -> RACSignal in self.signInSignal() }.subscribeNext { (any) in println(any) }
  • 31.
    Doing it right! signInButton.rac_signalForControlEvents(.TouchUpInside) .flattenMap { (any) -> RACSignal in self.signInSignal() }.subscribeNext { (any) in println(any) } flattenMap subscribes to a signal, returning the resultant events
  • 32.
    Side-effects signInButton.rac_signalForControlEvents(.TouchUpInside) .doNext{ (any) in self.signInButton.enabled = false; }.flattenMap { (any) -> RACSignal in self.signInSignal() }.subscribeNextAs { (success: NSNumber) in self.signInButton.enabled = true; self.handleSignInResult(success.boolValue) } Side-effects receive next events, but cannot mutate them
  • 33.
    Login pipeline doNext1 signInSignal1 BOOL1 UIBu3on1 BOOL1 rac_signalForControlEvents11 fla3enMap1 subscribeNext1
  • 34.
    ReactiveCocoa Made Simple • A signal emits events • next • error • completed • A signal can have multiple subscribers • Signals can emit multiple next events, optionally followed by either an error or completed
  • 35.
  • 37.
    [[[[[[[self requestAccessToTwitterSignal] then:^RACSignal*{ @strongify(self) return self.searchText.rac_textSignal; }] filter:^BOOL(NSString *text) { @strongify(self) return [self isValidSearchText:text]; }] throttle:0.5] flattenMap:^RACStream *(NSString *text) { @strongify(self) return [self signalForSearchWithText:text]; }] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDictionary *jsonSearchResult) { NSArray *statuses = jsonSearchResult[@"statuses"]; NSArray *tweets = [statuses linq_select:^id(id tweet) { return [RWTweet tweetWithStatus:tweet]; }]; [self.resultsViewController displayTweets:tweets]; } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];
  • 39.
  • 41.
    // a signalthat emits events when visibility changes let visibleStateChanged = RACObserve(self, "isVisible").skip(1) ! // filtered into visible and hidden signals let visibleSignal = visibleStateChanged.filter { $0.boolValue } let hiddenSignal = visibleStateChanged.filter { !$0.boolValue } ! // a signal that emits when an item has been visible for 1 second let fetchMetadata = visibleSignal.delay(1).takeUntil(hiddenSignal) ! fetchMetadata.subscribeNext { (next: AnyObject!) -> () in // fetch data }
  • 42.
    Resources • https://github.com/ReactiveCocoa/ReactiveCocoa • Read the Change Logs! • My Stuff … • http://www.raywenderlich.com/u/ColinEberhardt • https://github.com/ColinEberhardt • The code from this presentation • https://github.com/ColinEberhardt/ SwiftReactivePlayground
  • 43.
    ReactiveCocoa made Simplewith Swift! @ColinEberhardt ShinobiControls