ReactiveCocoa is a framework for building reactive applications using signals that emit events. It allows defining data flows where events are propagated through operations like map, filter, and flatten. Signals can represent UI controls, network requests, or other asynchronous events. This allows building reactive user interfaces where UI is updated automatically in response to data changes. Operations are chained fluently on signals to transform and combine events.
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
6. 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!
9. Get Reactive
let textSignal: 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!
• 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: {
// ...
})
12. 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!
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
• ReactiveCocoa allows 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 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
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/
26. 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
28. Reactive Button Click
signInButton.rac_signalForControlEvents(.TouchUpInside)
.subscribeNext {
(button) in
println("clicked")
}
Replaces target-action with a signal
30. Using the sign-in signal
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
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
41. // 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
}
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