SlideShare a Scribd company logo
1 of 97
Download to read offline
Building iOS apps
with ReactiveCocoa
About me
Eliasz Sawicki
Blog: http://eluss.github.io/
Twitter: @EliSawic
Let's begin
ReactiveCocoa
Functional Reactive
Programming
Wikipedia
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).
Reactive
Programming
Reactive Programming
• Working with asynchronous dataflow
• Reacting to state changes
Functional
Programming
Functional Programming
• Immutable
assert(f(x) == f(x))
A person
class Person {
var name: String
init(name: String) {
self.name = name
}
}
Mutable
func personWithReversedName(person: Person) -> Person {
person.name = String(person.name.characters.reverse())
return person
}
let person = Person(name: "John")
let reversedA = personWithReversedName(person)
print(reversedA.name) // nhoJ
let reversedB = personWithReversedName(person)
print(reversedB.name) // John
Immutable
func personWithReversedName(person: Person) -> Person {
let name = String(person.name.characters.reverse())
let newPerson = Person(name: name)
return newPerson
}
let person = Person(name: "John")
let reversedA = personWithReversedName(person)
print(reversedA.name) // nhoJ
let reversedB = personWithReversedName(person)
print(reversedB.name) // nhoJ
Functional Programming
• Immutable
• Stateless
Stateful
var value = 0
func increment() {
value += 1
}
Stateless
func increment(value: Int) -> Int {
return value + 1
}
Declarative
Imperative
vs
Declarative
Imperative
let array = [0, 1, 2, 3, 4, 5]
var evenNumbers = [Int]()
for element in array {
if element % 2 == 0 {
evenNumbers.append(element)
}
}
Declarative
let array = [0, 1, 2, 3, 4, 5]
let evenNumbers = array.filter { $0 % 2 == 0 }
Back to
ReactiveCocoa
Event streams
Event Stream
Event
Non-Terminating
• Next
Terminating
• Completed
• Failed
• Interrupted
Observer
Signal
What is it?
• Represents events over time
• Must be observed in order to access it's events
• Observing a signal does not trigger any side effects (push
based)
• No random access to events
Signal's lifetime
• Passes any number of Next events
• "Dies" when terminating event arrives
• Any new observer will receive Interrupted event
Observing
signal.observe { (event) in
print(event)
}
signal.observeNext { (value) in
print(value)
}
signal.observeCompleter {
print("Completed")
}
Creating Signals
Basic signal
Signal<String, NSError> { (observer) -> Disposable? in
observer.sendNext("test")
observer.sendCompleted()
return ActionDisposable(action: {
print("Signal disposed")
})
}
Pipe
let (signal, observer) = Signal<String, NoError>.pipe()
signal.observeNext({ text in
print(text)
})
signal.observeCompleted({
print("Test completed")
})
observer.sendNext("It's a test") // It's a test
observer.sendCompleted() // Test completed
SignalProducer
What is it?
• Represents tasks
• Creates signals
• Performs side effects
• Does not start it's work if not started
Injecting side effects
let producer = signalProducer
.on(started: {
print("Started")
}, event: { event in
print("Event: (event)")
}, failed: { error in
print("Failed: (error)")
}, completed: {
print("Completed")
}, interrupted: {
print("Interrupted")
}, terminated: {
print("Terminated")
}, disposed: {
print("Disposed")
}, next: { value in
print("Next: (value)")
})
Creating Signal
Producers
Basic Signal Producer
SignalProducer<String, NSError> { (observer, composite) in
composite.addDisposable({
print("Clearing work")
})
observer.sendNext("In Progres...")
observer.sendCompleted()
}
Buffer
let (producer, observer) = SignalProducer<String, NoError>.buffer(3)
observer.sendNext("test")
observer.sendCompleted()
producer.startWithSignal { (signal, disposable) in
signal.observeNext({ (text) in
print(text) // test
})
signal.observeCompleted({
print("Test completed") // Test completed
})
}
observer.sendNext("is interrupted")
observer.sendInterrupted()
producer.startWithSignal { (signal, disposable) in
signal.observeNext({ (text) in
print(text) // test, is interrupted
})
signal.observeInterrupted({
print("Test interrupted") // Test interrupted
})
}
Manipulating signals
Map
Map
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
let textSignal = numberSignal.map { (number) -> String in
return "Number is (number)"
}
numberSignal.observeNext { (number) in
print(number) // 5
}
textSignal.observeNext { (text) in
print(text) // Number is 5
}
observer.sendNext(5)
Filter
Filter
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
let fiveSignal = numberSignal.filter { (number) -> Bool in
return number == 5
}
numberSignal.observeNext { (number) in
print(number) // 6, 5
}
fiveSignal.observeNext { (number) in
print(number) // 5
}
observer.sendNext(6)
observer.sendNext(5)
Aggregating
Aggregating
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
let aggregtingSignal = numberSignal.reduce(0) { (currentValue, addedValue) -> Int in
return currentValue + addedValue
}
numberSignal.observeNext { (number) in
print(number) // 5, 6
}
aggregtingSignal.observeNext { (number) in
print("Aggregated (number)") // Aggregated 11
}
observer.sendNext(5)
observer.sendNext(6)
observer.sendCompleted()
Skip repeats
Skip repeats
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
numberSignal.observeNext { (number) in
print(number) // 1, 2, 2, 3
}
numberSignal.skipRepeats().observeNext { (number) in
print(number) // 1, 2, 3
}
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(2)
observer.sendNext(3)
Skip until
Skip until
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
numberSignal.observeNext { (number) in
print(number) // 5, 6
}
let (trigger, triggerObserver) = Signal<Void, NoError>.pipe()
numberSignal.skipUntil(trigger).observeNext { (number) in
print("Triggered (number)") // Triggered 6
}
observer.sendNext(5)
triggerObserver.sendNext()
observer.sendNext(6)
Collect
let (numberSignal, observer) = Signal<Int, NoError>.pipe()
numberSignal.observeNext { (number) in
print(number) // 1, 2, 3, 4, 5
}
numberSignal.collect { (values) -> Bool in
return values.reduce(0, combine: +) > 4
}.observeNext { (values) in
print(values) // [1, 2, 3], [4 ,5]
}
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendNext(5)
Manipulating
multiple signals
Combine latest
Combine latest
let (numberSignal, numberObserver) = Signal<Int, NoError>.pipe()
let (textSignal, textObserver) = Signal<String, NoError>.pipe()
combineLatest(numberSignal, textSignal).observeNext { (number, text) in
print("(text) - (number)")
}
numberObserver.sendNext(1) // Nothing printed
textObserver.sendNext("John") // John - 1
numberObserver.sendNext(2) // John - 2
textObserver.sendNext("Mary") // Mary - 2
Zip
Zip
let (menSignal, menObserver) = Signal<String, NoError>.pipe()
let (womenSignal, womenObserver) = Signal<String, NoError>.pipe()
let zippedSignal = zip(menSignal, womenSignal)
zippedSignal.observeNext { (man, woman) in
print("New couple - (man) and (woman)")
}
zippedSignal.observeCompleted({
print("Completed")
})
menObserver.sendNext("John") // Nothing printed
menObserver.sendNext("Tom") // Nothing printed
womenObserver.sendNext("Lisa") // New couple - John and Lisa
menObserver.sendNext("Greg") // Nothing printed
menObserver.sendCompleted()
womenObserver.sendNext("Sandra") // New couple - Tom and Sandra
womenObserver.sendNext("Mary") // New couple - Greg and Mary, Completed
Merge
Merge
let (menSignal, menObserver) = Signal<String, NoError>.pipe()
let (womenSignal, womenObserver) = Signal<String, NoError>.pipe()
let (peopleSignal, peopleObserver) = Signal<Signal<String, NoError>, NoError>.pipe()
peopleSignal.flatten(.Merge).observeNext { (name) in
print(name)
}
peopleObserver.sendNext(menSignal)
peopleObserver.sendNext(womenSignal)
menObserver.sendNext("John") // John
womenObserver.sendNext("Lisa") // Lisa
Handling errors
Catching errors
let (producer, observer) = SignalProducer<String, NSError>.buffer(5)
let error = NSError(domain: "domain", code: 0, userInfo: nil)
producer
.flatMapError { _ in SignalProducer<String, NoError>(value: "Default") }
.startWithNext { next in print(next) }
observer.sendNext("First") // prints "First"
observer.sendNext("Second") // prints "Second"
observer.sendFailed(error) // prints "Default"
Retry
var tries = 0 let limit = 2 let error = NSError(domain: "domain", code: 0, userInfo: nil)
let producer = SignalProducer<String, NSError> { (observer, _) in
if tries++ < limit {
observer.sendFailed(error)
} else {
observer.sendNext("Success")
observer.sendCompleted()
}
}
producer
.on(failed: {e in print("Failure")}).retry(2).start { event in // prints "Failure" twice
switch event {
case let .Next(next):
print(next) // prints "Success"
case let .Failed(error):
print("Failed: (error)")
}
}
Promoting
let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
let (lettersSignal, lettersObserver) = Signal<String, NSError>.pipe()
numbersSignal
.promoteErrors(NSError)
.combineLatestWith(lettersSignal)
Mapping errors
Properties
Properties
• AnyProperty
• ConstantProperty
• MutableProperty
• DynamicProperty
MutableProperty
let name = MutableProperty<String>("Bob")
name.producer.startWithNext { (text) in
print(text)
}
name.modify { (name) -> String in
return name + "!"
}
name.value = "Lisa"
DynamicProperty
let textProperty = DynamicProperty(object: textField, keyPath: "text")
textProperty.producer.startWithNext { (text) in
print(text)
}
textProperty.value = "Textfield text"
Bindings
Basic binding
let property = MutableProperty<String>("")
let (producer, _) = SignalProducer<String, NoError>.buffer(1)
let (signal, _) = Signal<String, NoError>.pipe()
property <~ producer
property <~ signal
Action
Create Action
let action = Action<Int, String, NSError>({ (number) -> SignalProducer<String, NSError> in
return SignalProducer<String, NSError> {observer, disposable in
observer.sendNext("Number is (number)")
observer.sendCompleted()
}
})
Create signal producer
let producer = action.apply(1)
Execute action
prodcuer.startWithSignal { (signal, disposable ) in
signal.observeNext({ (value) in
print("(value)")
})
signal.observeFailed({ (actionError) in
print("(actionError)")
})
}
Observing actions
let action = Action<Int, String, NSError>({ (number) -> SignalProducer<String, NSError> in
return SignalProducer<String, NSError> {observer, disposable in
observer.sendNext("Number is (number)")
observer.sendCompleted()
}
})
action.values.observe { (value) in
print("Value: (value)")
}
action.errors.observe { (error) in
print("Error: (error)")
}
action.events.observe { (event) in
print("Event: (event)")
}
action.apply(5).startWithSignal { (_ , _ ) in }
CocoaAction
Prepare Action
var text = MutableProperty<String>("Switch is on")
let switchControl = UISwitch()
let switchAction = Action<Bool, String, NoError>({
(isOn) -> SignalProducer<String, NoError> in
return SignalProducer<String, NoError> { observer, disposable in
observer.sendNext(isOn ? "Switch is on" : "Switch is off")
observer.sendCompleted()
}
})
Create CocoaAction
let switchCocoaAction = CocoaAction(switchAction, { (control) -> Bool in
let control = control as! UISwitch
return control.on
})
switchControl.addTarget(switchCocoaAction, action: CocoaAction.selector,
forControlEvents: .ValueChanged)
text <~ switchAction.values
Schedulers
• SchedulerType
• ImmediateScheduler
• UIScheduler
• DateSchedulerType
• QueueScheduler
• TestScheduler
Memory Management
Disposables
Task
let producer = SignalProducer<String, NoError> { (observer, composite) in
let date = NSDate().dateByAddingTimeInterval(10)
composite += QueueScheduler().scheduleAfter(date, action: {
print("Doing my work") // Doing my work
observer.sendNext("Test")
observer.sendCompleted()
})
}
producer.startWithSignal { (signal, disposable) in
signal.observeNext({ (value) in
print(value) // Test
})
signal.observeCompleted({
print("Work completed") // Work completed
})
}
Cancelling work
let producer = SignalProducer<String, NoError> { (observer, composite) in
let date = NSDate().dateByAddingTimeInterval(10)
composite += QueueScheduler().scheduleAfter(date, action: {
print("Doing my work") // Not printed
observer.sendNext("Test")
observer.sendCompleted()
})
}
producer.startWithSignal { (signal, disposable) in
signal.observeNext({ (value) in
print(value) // Not printed
})
signal.observeInterrupted({
print("Work interrupted") // Work interrupted
})
let date = NSDate().dateByAddingTimeInterval(2)
QueueScheduler().scheduleAfter(date, action: {
disposable.dispose()
})
}
Cleaning
let producer = SignalProducer<String, NoError> { (observer, composite) in
composite.addDisposable({
print("I'm done")
})
let date = NSDate().dateByAddingTimeInterval(4)
composite += QueueScheduler().scheduleAfter(date, action: {
print("Doing my work") // Not printed
})
}
producer.startWithSignal { (signal, disposable) in
signal.observeInterrupted({
print("Work interrupted")
})
let date = NSDate().dateByAddingTimeInterval(2)
QueueScheduler().scheduleAfter(date, action: {
disposable.dispose() // Work interrupted, I'm done
})
}
Disposing signal
let producer = SignalProducer<String, NoError> { (observer, composite) in
composite.addDisposable({
print("I'm done")
})
let date = NSDate().dateByAddingTimeInterval(5)
composite += QueueScheduler().scheduleAfter(date, action: {
print("Doing my work") // Not printed
})
}
producer.startWithSignal { (signal, disposable) in
let signalDisposable = signal.observeInterrupted({
print("Work interrupted") // Not printed
})
let date = NSDate().dateByAddingTimeInterval(2)
QueueScheduler().scheduleAfter(date, action: {
signalDisposable!.dispose()
})
let date2 = NSDate().dateByAddingTimeInterval(4)
QueueScheduler().scheduleAfter(date2, action: {
disposable.dispose()
})
}
Closures
What's the result?
var value = 10
let closure = {
let date = NSDate().dateByAddingTimeInterval(2)
QueueScheduler().scheduleAfter(date, action: {
print(value)
})
}
closure()
value = 20
Captured value
var value = 10
let closure = { [value] in
let date = NSDate().dateByAddingTimeInterval(2)
QueueScheduler().scheduleAfter(date, action: {
print(value)
})
}
closure()
value = 20
Weak, Strong,
Unowned...
Unowned
let closure = { [unowned self] in
self.label.text = "test"
}
Weak
let closure = { [weak self] in
guard let weakSelf = self else { return }
self.label.text = "test"
}
Rex
UIButton
let cocoaAction = CocoaAction(action) { _ in }
//without Rex
button.addTarget(cocoaAction, action: CocoaAction.selector,
forControlEvents: .TouchUpInside)
//with Rex extensions
button.rex_pressed.value = cocoaAction
UITextField, UILabel, MutableProperty
var titleValue = MutableProperty<String?>(nil)
//without Rex
textField.rac_textSignal().subscribeNext {
self.titleValue.value = $0 as? String
}
titleValue.producer.startWithNext {
self.label.text = $0
self.label.hidden = $0?.characters.count < 5
}
//with Rex
titleValue <~ textField.rex_text
titleLabel.rex_text <~ titleValue
titleLabel.rex_hidden <~ titleValue.producer.map(
{ $0?.characters.count < 5 }
)
Let's see it in action

More Related Content

What's hot

JDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easyJDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easy
José Paumard
 

What's hot (20)

Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2
 
Promise of an API
Promise of an APIPromise of an API
Promise of an API
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
 
Scala 2 + 2 > 4
Scala 2 + 2 > 4Scala 2 + 2 > 4
Scala 2 + 2 > 4
 
C# Advanced L04-Threading
C# Advanced L04-ThreadingC# Advanced L04-Threading
C# Advanced L04-Threading
 
Akka.NET streams and reactive streams
Akka.NET streams and reactive streamsAkka.NET streams and reactive streams
Akka.NET streams and reactive streams
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
 
Rust
RustRust
Rust
 
Behind modern concurrency primitives
Behind modern concurrency primitivesBehind modern concurrency primitives
Behind modern concurrency primitives
 
Rxjs ppt
Rxjs pptRxjs ppt
Rxjs ppt
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
XpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp way
 
Angular2 rxjs
Angular2 rxjsAngular2 rxjs
Angular2 rxjs
 
"Kotlin и rx в android" Дмитрий Воронин (Avito)
"Kotlin и rx в android" Дмитрий Воронин  (Avito)"Kotlin и rx в android" Дмитрий Воронин  (Avito)
"Kotlin и rx в android" Дмитрий Воронин (Avito)
 
Java SE 8 for Java EE developers
Java SE 8 for Java EE developersJava SE 8 for Java EE developers
Java SE 8 for Java EE developers
 
Transition graph using free monads and existentials
Transition graph using free monads and existentialsTransition graph using free monads and existentials
Transition graph using free monads and existentials
 
JDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easyJDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easy
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6
 
Functional Stream Processing with Scalaz-Stream
Functional Stream Processing with Scalaz-StreamFunctional Stream Processing with Scalaz-Stream
Functional Stream Processing with Scalaz-Stream
 

Viewers also liked

Pintamania power
Pintamania powerPintamania power
Pintamania power
pituteam
 
Assignment four the presenter's fieldbook
Assignment four the presenter's fieldbookAssignment four the presenter's fieldbook
Assignment four the presenter's fieldbook
juniorvice04
 

Viewers also liked (19)

Impostor syndrome and individual competence - Jessica Rose - Codemotion Amste...
Impostor syndrome and individual competence - Jessica Rose - Codemotion Amste...Impostor syndrome and individual competence - Jessica Rose - Codemotion Amste...
Impostor syndrome and individual competence - Jessica Rose - Codemotion Amste...
 
Introduction to Angular js 2.0
Introduction to Angular js 2.0Introduction to Angular js 2.0
Introduction to Angular js 2.0
 
Hola,hola
Hola,holaHola,hola
Hola,hola
 
Pintamania power
Pintamania powerPintamania power
Pintamania power
 
Abduallazeez CV
Abduallazeez CVAbduallazeez CV
Abduallazeez CV
 
Jessica fox
Jessica foxJessica fox
Jessica fox
 
Assignment four the presenter's fieldbook
Assignment four the presenter's fieldbookAssignment four the presenter's fieldbook
Assignment four the presenter's fieldbook
 
Unidirectional Data Flow in Swift
Unidirectional Data Flow in SwiftUnidirectional Data Flow in Swift
Unidirectional Data Flow in Swift
 
An overview of JavaScript
An overview of JavaScriptAn overview of JavaScript
An overview of JavaScript
 
GTS Episode 1: Reactive programming in the wild
GTS Episode 1: Reactive programming in the wildGTS Episode 1: Reactive programming in the wild
GTS Episode 1: Reactive programming in the wild
 
Reconsidering english language teaching in vernacular medium schools of india...
Reconsidering english language teaching in vernacular medium schools of india...Reconsidering english language teaching in vernacular medium schools of india...
Reconsidering english language teaching in vernacular medium schools of india...
 
ENJ-1-337 Presentación Módulo II Delitos Electrónicos (2)
ENJ-1-337 Presentación Módulo II Delitos Electrónicos (2)ENJ-1-337 Presentación Módulo II Delitos Electrónicos (2)
ENJ-1-337 Presentación Módulo II Delitos Electrónicos (2)
 
Hybrid mobile and Ionic
Hybrid mobile and IonicHybrid mobile and Ionic
Hybrid mobile and Ionic
 
Higher order infrastructure: from Docker basics to cluster management - Nicol...
Higher order infrastructure: from Docker basics to cluster management - Nicol...Higher order infrastructure: from Docker basics to cluster management - Nicol...
Higher order infrastructure: from Docker basics to cluster management - Nicol...
 
Introduction to Functional Reactive Programming
Introduction to Functional Reactive ProgrammingIntroduction to Functional Reactive Programming
Introduction to Functional Reactive Programming
 
Hybrid vs native mobile development – how to choose a tech stack
Hybrid vs native mobile development – how to choose a tech stackHybrid vs native mobile development – how to choose a tech stack
Hybrid vs native mobile development – how to choose a tech stack
 
Ionic Mobile Applications - Hybrid Mobile Applications Without Compromises
Ionic Mobile Applications - Hybrid Mobile Applications Without CompromisesIonic Mobile Applications - Hybrid Mobile Applications Without Compromises
Ionic Mobile Applications - Hybrid Mobile Applications Without Compromises
 
La Magia de Disney, presentación René Carrasco
La Magia de Disney, presentación René CarrascoLa Magia de Disney, presentación René Carrasco
La Magia de Disney, presentación René Carrasco
 
Кластер БГУИР: расширенные возможности
Кластер БГУИР: расширенные возможностиКластер БГУИР: расширенные возможности
Кластер БГУИР: расширенные возможности
 

Similar to ReactiveCocoa workshop

Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 
Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай Мозговой
Sigma Software
 
オープンデータを使ったモバイルアプリ開発(応用編)
オープンデータを使ったモバイルアプリ開発(応用編)オープンデータを使ったモバイルアプリ開発(応用編)
オープンデータを使ったモバイルアプリ開発(応用編)
Takayuki Goto
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
riue
 
81818088 isc-class-xii-computer-science-project-java-programs
81818088 isc-class-xii-computer-science-project-java-programs81818088 isc-class-xii-computer-science-project-java-programs
81818088 isc-class-xii-computer-science-project-java-programs
Abhishek Jena
 

Similar to ReactiveCocoa workshop (20)

ProgrammingwithGOLang
ProgrammingwithGOLangProgrammingwithGOLang
ProgrammingwithGOLang
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my day
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJS
 
Geeks Anonymes - Le langage Go
Geeks Anonymes - Le langage GoGeeks Anonymes - Le langage Go
Geeks Anonymes - Le langage Go
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
golang_getting_started.pptx
golang_getting_started.pptxgolang_getting_started.pptx
golang_getting_started.pptx
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming ii
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
Introduction to go
Introduction to goIntroduction to go
Introduction to go
 
Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай Мозговой
 
Swift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdfSwift 5.1 Language Guide Notes.pdf
Swift 5.1 Language Guide Notes.pdf
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on Android
 
オープンデータを使ったモバイルアプリ開発(応用編)
オープンデータを使ったモバイルアプリ開発(応用編)オープンデータを使ったモバイルアプリ開発(応用編)
オープンデータを使ったモバイルアプリ開発(応用編)
 
III MCS python lab (1).pdf
III MCS python lab (1).pdfIII MCS python lab (1).pdf
III MCS python lab (1).pdf
 
Reactive Programming Patterns with RxSwift
Reactive Programming Patterns with RxSwiftReactive Programming Patterns with RxSwift
Reactive Programming Patterns with RxSwift
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
 
81818088 isc-class-xii-computer-science-project-java-programs
81818088 isc-class-xii-computer-science-project-java-programs81818088 isc-class-xii-computer-science-project-java-programs
81818088 isc-class-xii-computer-science-project-java-programs
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
Wprowadzenie do technologii Big Data / Intro to Big Data Ecosystem
Wprowadzenie do technologii Big Data / Intro to Big Data EcosystemWprowadzenie do technologii Big Data / Intro to Big Data Ecosystem
Wprowadzenie do technologii Big Data / Intro to Big Data Ecosystem
 
What's in Kotlin for us - Alexandre Greschon, MyHeritage
What's in Kotlin for us - Alexandre Greschon, MyHeritageWhat's in Kotlin for us - Alexandre Greschon, MyHeritage
What's in Kotlin for us - Alexandre Greschon, MyHeritage
 

More from Eliasz Sawicki

More from Eliasz Sawicki (14)

Redux - 4Developers
Redux - 4DevelopersRedux - 4Developers
Redux - 4Developers
 
Eliasz sawickimeetupit
Eliasz sawickimeetupitEliasz sawickimeetupit
Eliasz sawickimeetupit
 
Developing more in less time
Developing more in less timeDeveloping more in less time
Developing more in less time
 
The art-of-developing-more-in-less-time-berlin
The art-of-developing-more-in-less-time-berlinThe art-of-developing-more-in-less-time-berlin
The art-of-developing-more-in-less-time-berlin
 
Tech fest
Tech festTech fest
Tech fest
 
Introduction to react native
Introduction to react nativeIntroduction to react native
Introduction to react native
 
Doing more in less time - Mobiconf
Doing more in less time - MobiconfDoing more in less time - Mobiconf
Doing more in less time - Mobiconf
 
iOSCon
iOSConiOSCon
iOSCon
 
Code europe
Code europeCode europe
Code europe
 
Introduction To Functional Reactive Programming Poznan
Introduction To Functional Reactive Programming PoznanIntroduction To Functional Reactive Programming Poznan
Introduction To Functional Reactive Programming Poznan
 
Introduction to Functional Reactive Programming
Introduction to Functional Reactive ProgrammingIntroduction to Functional Reactive Programming
Introduction to Functional Reactive Programming
 
Time traveling with ReSwift
Time traveling with ReSwiftTime traveling with ReSwift
Time traveling with ReSwift
 
Calabash
CalabashCalabash
Calabash
 
ReSwift CocoaHeads Tricity
ReSwift CocoaHeads TricityReSwift CocoaHeads Tricity
ReSwift CocoaHeads Tricity
 

Recently uploaded

Structuring Teams and Portfolios for Success
Structuring Teams and Portfolios for SuccessStructuring Teams and Portfolios for Success
Structuring Teams and Portfolios for Success
UXDXConf
 
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
panagenda
 

Recently uploaded (20)

A Business-Centric Approach to Design System Strategy
A Business-Centric Approach to Design System StrategyA Business-Centric Approach to Design System Strategy
A Business-Centric Approach to Design System Strategy
 
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
 
Overview of Hyperledger Foundation
Overview of Hyperledger FoundationOverview of Hyperledger Foundation
Overview of Hyperledger Foundation
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
Intro in Product Management - Коротко про професію продакт менеджера
Intro in Product Management - Коротко про професію продакт менеджераIntro in Product Management - Коротко про професію продакт менеджера
Intro in Product Management - Коротко про професію продакт менеджера
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at Comcast
 
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
PLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. StartupsPLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. Startups
 
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
Choosing the Right FDO Deployment Model for Your Application _ Geoffrey at In...
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 
Structuring Teams and Portfolios for Success
Structuring Teams and Portfolios for SuccessStructuring Teams and Portfolios for Success
Structuring Teams and Portfolios for Success
 
Syngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdfSyngulon - Selection technology May 2024.pdf
Syngulon - Selection technology May 2024.pdf
 
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
 
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
 
Microsoft CSP Briefing Pre-Engagement - Questionnaire
Microsoft CSP Briefing Pre-Engagement - QuestionnaireMicrosoft CSP Briefing Pre-Engagement - Questionnaire
Microsoft CSP Briefing Pre-Engagement - Questionnaire
 
Enterprise Knowledge Graphs - Data Summit 2024
Enterprise Knowledge Graphs - Data Summit 2024Enterprise Knowledge Graphs - Data Summit 2024
Enterprise Knowledge Graphs - Data Summit 2024
 
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
 
What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
 

ReactiveCocoa workshop

  • 1. Building iOS apps with ReactiveCocoa
  • 2. About me Eliasz Sawicki Blog: http://eluss.github.io/ Twitter: @EliSawic
  • 6. Wikipedia 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).
  • 8. Reactive Programming • Working with asynchronous dataflow • Reacting to state changes
  • 12. A person class Person { var name: String init(name: String) { self.name = name } }
  • 13. Mutable func personWithReversedName(person: Person) -> Person { person.name = String(person.name.characters.reverse()) return person } let person = Person(name: "John") let reversedA = personWithReversedName(person) print(reversedA.name) // nhoJ let reversedB = personWithReversedName(person) print(reversedB.name) // John
  • 14. Immutable func personWithReversedName(person: Person) -> Person { let name = String(person.name.characters.reverse()) let newPerson = Person(name: name) return newPerson } let person = Person(name: "John") let reversedA = personWithReversedName(person) print(reversedA.name) // nhoJ let reversedB = personWithReversedName(person) print(reversedB.name) // nhoJ
  • 16. Stateful var value = 0 func increment() { value += 1 }
  • 17. Stateless func increment(value: Int) -> Int { return value + 1 }
  • 20. Imperative let array = [0, 1, 2, 3, 4, 5] var evenNumbers = [Int]() for element in array { if element % 2 == 0 { evenNumbers.append(element) } }
  • 21. Declarative let array = [0, 1, 2, 3, 4, 5] let evenNumbers = array.filter { $0 % 2 == 0 }
  • 25. Event
  • 29. What is it? • Represents events over time • Must be observed in order to access it's events • Observing a signal does not trigger any side effects (push based) • No random access to events
  • 30. Signal's lifetime • Passes any number of Next events • "Dies" when terminating event arrives • Any new observer will receive Interrupted event
  • 31. Observing signal.observe { (event) in print(event) } signal.observeNext { (value) in print(value) } signal.observeCompleter { print("Completed") }
  • 33. Basic signal Signal<String, NSError> { (observer) -> Disposable? in observer.sendNext("test") observer.sendCompleted() return ActionDisposable(action: { print("Signal disposed") }) }
  • 34. Pipe
  • 35. let (signal, observer) = Signal<String, NoError>.pipe() signal.observeNext({ text in print(text) }) signal.observeCompleted({ print("Test completed") }) observer.sendNext("It's a test") // It's a test observer.sendCompleted() // Test completed
  • 37. What is it? • Represents tasks • Creates signals • Performs side effects • Does not start it's work if not started
  • 38. Injecting side effects let producer = signalProducer .on(started: { print("Started") }, event: { event in print("Event: (event)") }, failed: { error in print("Failed: (error)") }, completed: { print("Completed") }, interrupted: { print("Interrupted") }, terminated: { print("Terminated") }, disposed: { print("Disposed") }, next: { value in print("Next: (value)") })
  • 40. Basic Signal Producer SignalProducer<String, NSError> { (observer, composite) in composite.addDisposable({ print("Clearing work") }) observer.sendNext("In Progres...") observer.sendCompleted() }
  • 41. Buffer let (producer, observer) = SignalProducer<String, NoError>.buffer(3) observer.sendNext("test") observer.sendCompleted() producer.startWithSignal { (signal, disposable) in signal.observeNext({ (text) in print(text) // test }) signal.observeCompleted({ print("Test completed") // Test completed }) } observer.sendNext("is interrupted") observer.sendInterrupted() producer.startWithSignal { (signal, disposable) in signal.observeNext({ (text) in print(text) // test, is interrupted }) signal.observeInterrupted({ print("Test interrupted") // Test interrupted }) }
  • 43. Map
  • 44. Map let (numberSignal, observer) = Signal<Int, NoError>.pipe() let textSignal = numberSignal.map { (number) -> String in return "Number is (number)" } numberSignal.observeNext { (number) in print(number) // 5 } textSignal.observeNext { (text) in print(text) // Number is 5 } observer.sendNext(5)
  • 46. Filter let (numberSignal, observer) = Signal<Int, NoError>.pipe() let fiveSignal = numberSignal.filter { (number) -> Bool in return number == 5 } numberSignal.observeNext { (number) in print(number) // 6, 5 } fiveSignal.observeNext { (number) in print(number) // 5 } observer.sendNext(6) observer.sendNext(5)
  • 48. Aggregating let (numberSignal, observer) = Signal<Int, NoError>.pipe() let aggregtingSignal = numberSignal.reduce(0) { (currentValue, addedValue) -> Int in return currentValue + addedValue } numberSignal.observeNext { (number) in print(number) // 5, 6 } aggregtingSignal.observeNext { (number) in print("Aggregated (number)") // Aggregated 11 } observer.sendNext(5) observer.sendNext(6) observer.sendCompleted()
  • 50. Skip repeats let (numberSignal, observer) = Signal<Int, NoError>.pipe() numberSignal.observeNext { (number) in print(number) // 1, 2, 2, 3 } numberSignal.skipRepeats().observeNext { (number) in print(number) // 1, 2, 3 } observer.sendNext(1) observer.sendNext(2) observer.sendNext(2) observer.sendNext(3)
  • 52. Skip until let (numberSignal, observer) = Signal<Int, NoError>.pipe() numberSignal.observeNext { (number) in print(number) // 5, 6 } let (trigger, triggerObserver) = Signal<Void, NoError>.pipe() numberSignal.skipUntil(trigger).observeNext { (number) in print("Triggered (number)") // Triggered 6 } observer.sendNext(5) triggerObserver.sendNext() observer.sendNext(6)
  • 53. Collect let (numberSignal, observer) = Signal<Int, NoError>.pipe() numberSignal.observeNext { (number) in print(number) // 1, 2, 3, 4, 5 } numberSignal.collect { (values) -> Bool in return values.reduce(0, combine: +) > 4 }.observeNext { (values) in print(values) // [1, 2, 3], [4 ,5] } observer.sendNext(1) observer.sendNext(2) observer.sendNext(3) observer.sendNext(4) observer.sendNext(5)
  • 57. let (numberSignal, numberObserver) = Signal<Int, NoError>.pipe() let (textSignal, textObserver) = Signal<String, NoError>.pipe() combineLatest(numberSignal, textSignal).observeNext { (number, text) in print("(text) - (number)") } numberObserver.sendNext(1) // Nothing printed textObserver.sendNext("John") // John - 1 numberObserver.sendNext(2) // John - 2 textObserver.sendNext("Mary") // Mary - 2
  • 58. Zip
  • 59. Zip let (menSignal, menObserver) = Signal<String, NoError>.pipe() let (womenSignal, womenObserver) = Signal<String, NoError>.pipe() let zippedSignal = zip(menSignal, womenSignal) zippedSignal.observeNext { (man, woman) in print("New couple - (man) and (woman)") } zippedSignal.observeCompleted({ print("Completed") }) menObserver.sendNext("John") // Nothing printed menObserver.sendNext("Tom") // Nothing printed womenObserver.sendNext("Lisa") // New couple - John and Lisa menObserver.sendNext("Greg") // Nothing printed menObserver.sendCompleted() womenObserver.sendNext("Sandra") // New couple - Tom and Sandra womenObserver.sendNext("Mary") // New couple - Greg and Mary, Completed
  • 60. Merge
  • 61. Merge let (menSignal, menObserver) = Signal<String, NoError>.pipe() let (womenSignal, womenObserver) = Signal<String, NoError>.pipe() let (peopleSignal, peopleObserver) = Signal<Signal<String, NoError>, NoError>.pipe() peopleSignal.flatten(.Merge).observeNext { (name) in print(name) } peopleObserver.sendNext(menSignal) peopleObserver.sendNext(womenSignal) menObserver.sendNext("John") // John womenObserver.sendNext("Lisa") // Lisa
  • 63. Catching errors let (producer, observer) = SignalProducer<String, NSError>.buffer(5) let error = NSError(domain: "domain", code: 0, userInfo: nil) producer .flatMapError { _ in SignalProducer<String, NoError>(value: "Default") } .startWithNext { next in print(next) } observer.sendNext("First") // prints "First" observer.sendNext("Second") // prints "Second" observer.sendFailed(error) // prints "Default"
  • 64. Retry var tries = 0 let limit = 2 let error = NSError(domain: "domain", code: 0, userInfo: nil) let producer = SignalProducer<String, NSError> { (observer, _) in if tries++ < limit { observer.sendFailed(error) } else { observer.sendNext("Success") observer.sendCompleted() } } producer .on(failed: {e in print("Failure")}).retry(2).start { event in // prints "Failure" twice switch event { case let .Next(next): print(next) // prints "Success" case let .Failed(error): print("Failed: (error)") } }
  • 65. Promoting let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe() let (lettersSignal, lettersObserver) = Signal<String, NSError>.pipe() numbersSignal .promoteErrors(NSError) .combineLatestWith(lettersSignal)
  • 68. Properties • AnyProperty • ConstantProperty • MutableProperty • DynamicProperty
  • 69. MutableProperty let name = MutableProperty<String>("Bob") name.producer.startWithNext { (text) in print(text) } name.modify { (name) -> String in return name + "!" } name.value = "Lisa"
  • 70. DynamicProperty let textProperty = DynamicProperty(object: textField, keyPath: "text") textProperty.producer.startWithNext { (text) in print(text) } textProperty.value = "Textfield text"
  • 72. Basic binding let property = MutableProperty<String>("") let (producer, _) = SignalProducer<String, NoError>.buffer(1) let (signal, _) = Signal<String, NoError>.pipe() property <~ producer property <~ signal
  • 74. Create Action let action = Action<Int, String, NSError>({ (number) -> SignalProducer<String, NSError> in return SignalProducer<String, NSError> {observer, disposable in observer.sendNext("Number is (number)") observer.sendCompleted() } })
  • 75. Create signal producer let producer = action.apply(1)
  • 76. Execute action prodcuer.startWithSignal { (signal, disposable ) in signal.observeNext({ (value) in print("(value)") }) signal.observeFailed({ (actionError) in print("(actionError)") }) }
  • 77. Observing actions let action = Action<Int, String, NSError>({ (number) -> SignalProducer<String, NSError> in return SignalProducer<String, NSError> {observer, disposable in observer.sendNext("Number is (number)") observer.sendCompleted() } }) action.values.observe { (value) in print("Value: (value)") } action.errors.observe { (error) in print("Error: (error)") } action.events.observe { (event) in print("Event: (event)") } action.apply(5).startWithSignal { (_ , _ ) in }
  • 79. Prepare Action var text = MutableProperty<String>("Switch is on") let switchControl = UISwitch() let switchAction = Action<Bool, String, NoError>({ (isOn) -> SignalProducer<String, NoError> in return SignalProducer<String, NoError> { observer, disposable in observer.sendNext(isOn ? "Switch is on" : "Switch is off") observer.sendCompleted() } })
  • 80. Create CocoaAction let switchCocoaAction = CocoaAction(switchAction, { (control) -> Bool in let control = control as! UISwitch return control.on }) switchControl.addTarget(switchCocoaAction, action: CocoaAction.selector, forControlEvents: .ValueChanged) text <~ switchAction.values
  • 81. Schedulers • SchedulerType • ImmediateScheduler • UIScheduler • DateSchedulerType • QueueScheduler • TestScheduler
  • 84. Task let producer = SignalProducer<String, NoError> { (observer, composite) in let date = NSDate().dateByAddingTimeInterval(10) composite += QueueScheduler().scheduleAfter(date, action: { print("Doing my work") // Doing my work observer.sendNext("Test") observer.sendCompleted() }) } producer.startWithSignal { (signal, disposable) in signal.observeNext({ (value) in print(value) // Test }) signal.observeCompleted({ print("Work completed") // Work completed }) }
  • 85. Cancelling work let producer = SignalProducer<String, NoError> { (observer, composite) in let date = NSDate().dateByAddingTimeInterval(10) composite += QueueScheduler().scheduleAfter(date, action: { print("Doing my work") // Not printed observer.sendNext("Test") observer.sendCompleted() }) } producer.startWithSignal { (signal, disposable) in signal.observeNext({ (value) in print(value) // Not printed }) signal.observeInterrupted({ print("Work interrupted") // Work interrupted }) let date = NSDate().dateByAddingTimeInterval(2) QueueScheduler().scheduleAfter(date, action: { disposable.dispose() }) }
  • 86. Cleaning let producer = SignalProducer<String, NoError> { (observer, composite) in composite.addDisposable({ print("I'm done") }) let date = NSDate().dateByAddingTimeInterval(4) composite += QueueScheduler().scheduleAfter(date, action: { print("Doing my work") // Not printed }) } producer.startWithSignal { (signal, disposable) in signal.observeInterrupted({ print("Work interrupted") }) let date = NSDate().dateByAddingTimeInterval(2) QueueScheduler().scheduleAfter(date, action: { disposable.dispose() // Work interrupted, I'm done }) }
  • 87. Disposing signal let producer = SignalProducer<String, NoError> { (observer, composite) in composite.addDisposable({ print("I'm done") }) let date = NSDate().dateByAddingTimeInterval(5) composite += QueueScheduler().scheduleAfter(date, action: { print("Doing my work") // Not printed }) } producer.startWithSignal { (signal, disposable) in let signalDisposable = signal.observeInterrupted({ print("Work interrupted") // Not printed }) let date = NSDate().dateByAddingTimeInterval(2) QueueScheduler().scheduleAfter(date, action: { signalDisposable!.dispose() }) let date2 = NSDate().dateByAddingTimeInterval(4) QueueScheduler().scheduleAfter(date2, action: { disposable.dispose() }) }
  • 89. What's the result? var value = 10 let closure = { let date = NSDate().dateByAddingTimeInterval(2) QueueScheduler().scheduleAfter(date, action: { print(value) }) } closure() value = 20
  • 90. Captured value var value = 10 let closure = { [value] in let date = NSDate().dateByAddingTimeInterval(2) QueueScheduler().scheduleAfter(date, action: { print(value) }) } closure() value = 20
  • 92. Unowned let closure = { [unowned self] in self.label.text = "test" }
  • 93. Weak let closure = { [weak self] in guard let weakSelf = self else { return } self.label.text = "test" }
  • 94. Rex
  • 95. UIButton let cocoaAction = CocoaAction(action) { _ in } //without Rex button.addTarget(cocoaAction, action: CocoaAction.selector, forControlEvents: .TouchUpInside) //with Rex extensions button.rex_pressed.value = cocoaAction
  • 96. UITextField, UILabel, MutableProperty var titleValue = MutableProperty<String?>(nil) //without Rex textField.rac_textSignal().subscribeNext { self.titleValue.value = $0 as? String } titleValue.producer.startWithNext { self.label.text = $0 self.label.hidden = $0?.characters.count < 5 } //with Rex titleValue <~ textField.rex_text titleLabel.rex_text <~ titleValue titleLabel.rex_hidden <~ titleValue.producer.map( { $0?.characters.count < 5 } )
  • 97. Let's see it in action