Upload
eliasz-sawicki
View
75
Download
2
Embed Size (px)
Citation preview
About me
Eliasz SawickiBlog: www.eliaszsawicki.comTwitter: @EliSawic
@EliSawic
Introduction to Functional Reactive
Programming
@EliSawic
Agenda• What is Functional Reactive Programming?
• Let's take a look at ReactiveCocoa
• Working with streams
• Real life example
• Conclusion
@EliSawic
Functional Reactive Programming
@EliSawic
WikipediaFunctional 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).
@EliSawic
Reactive Programming
@EliSawic
Asynchronous Dataflow
@EliSawic
Reacting to state changes
@EliSawic
Functional Programming
@EliSawic
Immutable
@EliSawic
assert(f(x) == f(x))
@EliSawic
A personclass Person { let name: String let phoneNumber: String init(name: String, phoneNumber: String) { self.name = name self.phoneNumber = phoneNumber }}
class MobilePhone { func call(person: Person) -> Bool { // implementation }}
@EliSawic
Mutablelet mobilePhone = MobilePhone()let john = Person(name: "John", phoneNumber: "123456789")
func makeAPhoneCall(device: Phone, person: Person, countryCode: String) -> Bool { person.phoneNumber = countryCode + person.phoneNumber let success = device.call(person) return success}
makeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // falsemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // false
@EliSawic
Immutablelet mobilePhone = MobilePhone()let john = Person(name: "John", phoneNumber: "123456789")
func makeAPhoneCall(device: Phone, person: Person, countryCode: String) -> Bool { let prefixedPhoneNumber = countryCode + person.phoneNumber let newPerson = Person(name: person.name, phoneNumber: prefixedPhoneNumber) let success = device.call(newPerson) return success}
makeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // truemakeAPhoneCall(device: mobilePhone, person: john, countryCode: "+48") // true
@EliSawic
Stateless
@EliSawic
Statefulvar value = 0
func increment() -> Int { value += 1 return value}
@EliSawic
Statelessfunc increment(value: Int) -> Int { return value + 1}
@EliSawic
Functional Reactive Programming
@EliSawic
Imperative vs
Declarative
@EliSawic
Imperative
@EliSawic
Imperativelet array = [0, 1, 2, 3, 4, 5]var evenNumbers = [Int]()for element in array { if element % 2 == 0 { evenNumbers.append(element) }}
@EliSawic
Declarative
@EliSawic
Declarativelet array = [0, 1, 2, 3, 4, 5]let evenNumbers = array.filter { $0 % 2 == 0 }
@EliSawic
ReactiveCocoa
@EliSawic
Event streams
@EliSawic
Event Stream
@EliSawic
Event
@EliSawic
Non-Terminating• Next
@EliSawic
Terminating
• Completed
• Failed
• Interrupted (Reactive Cocoa)
@EliSawic
Signal
@EliSawic
Events over time
@EliSawic
No side effects
@EliSawic
No random access to events
@EliSawic
Must be observed in order to access it's
events
@EliSawic
Hot
@EliSawic
Signal's lifetime• Passes any number of Next events
• "Dies" when terminating event
• Any new observer will receive Interrupted event
@EliSawic
Observinglet someWork = Signal<Int, NoError> { observer in observer.sendNext(1) observer.sendNext(2) .... observer.sendNext(1000000) observer.sendCompleted()}
signal.observe { (event) in print(event)}signal.observeNext { (value) in print(value)}
signal.observeCompleted { print("Completed")}
@EliSawic
Pipe
@EliSawic
Pipelet (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 testobserver.sendCompleted() // Test completed
@EliSawic
SignalProducer
@EliSawic
Represents tasks
@EliSawic
Creates signal
@EliSawic
Possible side effects
@EliSawic
Does not start it's work if not started
@EliSawic
Cold
@EliSawic
Using Signal Producerlet producer = SignalProducer<String, NSError> { (observer, composite) in observer.sendNext("In Progress...") // ...... observer.sendCompleted()}
producer.startWithSignal { (signal, _) in signal.observeNext({ (text) in print(text) // In Progress... }) signal.observeCompleted({ print("Test completed") // Test completed })}
@EliSawic
Cold vs Hot
@EliSawic
Properties
@EliSawic
MutablePropertylet name = MutableProperty("Bob")
name.producer.startWithNext { (text) in print(text)}
name.value = "Lisa"
@EliSawic
Bindings
@EliSawic
Basic bindinglet property = MutableProperty<String>("")let (signal, _) = Signal<String, NoError>.pipe()property <~ signal
@EliSawic
Schedulers
@EliSawic
Memory Management
@EliSawic
Disposables
@EliSawic
Manipulating signals
@EliSawic
Map
@EliSawic
Filter
@EliSawic
Aggregating
@EliSawic
Skip repeats
@EliSawic
Manipulating multiple signals
@EliSawic
Combine latest
@EliSawic
Zip
@EliSawic
Merge
@EliSawic
Chaining operators
@EliSawic
Chain them all!let newSignalX = signalX.skipRepeats() .filter { x > 2 } .map { x * 10 }
let newSignalY = signalY.filter { x > 10 }
let combined = combineLatest(newSignalX, newSignalY)
combined.observeNext { xValue, yValue in print("Update: \(xValue) : \(yValue)")}
@EliSawic
Example
@EliSawic
@EliSawic
@EliSawic
@EliSawic
@EliSawic
@EliSawic
How does it work?
@EliSawic
Is name valid?let isValidName = nameSignal.map { (name) -> Bool in return input.characters.count > 2}
@EliSawic
Is mail valid?let isValidMail = mailSignal.map { (mail) -> Bool in let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) return emailTest.evaluateWithObject(mail)}
@EliSawic
Combine Latestlet formData = combineLatest(isValidName, isValidSurname, isValidMail)
@EliSawic
Is form valid?disposables += isFormValid <~ formData.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail}
@EliSawic
Button statelet producer = isFormValid.producer.skipRepeats()disposables += producer.startWithNext {[unowned self] (isValid) in self.updateAcceptButtonWithState(isValid)}
@EliSawic
Conclusion
@EliSawic
Thank you for your attention!
@EliSawic