Successfully reported this slideshow.
Your SlideShare is downloading. ×

Tech fest

More Related Content

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Tech fest

  1. 1. Functional Reactive Programming with ReactiveCocoa @EliSawic
  2. 2. About me Eliasz Sawicki Blog: www.eliaszsawicki.com Twitter: @EliSawic @EliSawic
  3. 3. Agenda • What is functional reactive programming? • Working with streams • ReactiveCocoa - Thinking in signals • Example @EliSawic
  4. 4. Functional Reactive Programming @EliSawic
  5. 5. 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). @EliSawic
  6. 6. Reactive Programming @EliSawic
  7. 7. C = A + B @EliSawic
  8. 8. C = A + B A B C 1 2 3 @EliSawic
  9. 9. C = A + B A B C 1 2 3 1 5 6 @EliSawic
  10. 10. Functional Programming @EliSawic
  11. 11. Blocks @EliSawic
  12. 12. Functional Reactive Programming? @EliSawic
  13. 13. Reactive Programming? @EliSawic
  14. 14. Imperative vs Declarative @EliSawic
  15. 15. Imperative @EliSawic
  16. 16. Imperative let array = [0, 1, 2, 3, 4, 5] var evenNumbers = [Int]() for element in array { if element % 2 == 0 { evenNumbers.append(element) } } @EliSawic
  17. 17. Declarative @EliSawic
  18. 18. Declarative let array = [0, 1, 2, 3, 4, 5] let evenNumbers = array.filter { $0 % 2 == 0 } @EliSawic
  19. 19. Working with streams @EliSawic
  20. 20. Stream @EliSawic
  21. 21. Manipulating streams @EliSawic
  22. 22. Map @EliSawic
  23. 23. Filter @EliSawic
  24. 24. Aggregating @EliSawic
  25. 25. Manipulating multiple streams @EliSawic
  26. 26. Combine latest @EliSawic
  27. 27. Composable @EliSawic
  28. 28. Composable stream.filter {} @EliSawic
  29. 29. Composable stream.filter {} .map {} @EliSawic
  30. 30. Composable stream.filter {} .map {} .reduce {} @EliSawic
  31. 31. Composable stream.filter {} .map {} .reduce {} .map {} @EliSawic
  32. 32. www.rxmarbles.com @EliSawic
  33. 33. ReactiveSwift @EliSawic
  34. 34. ReactiveCocoa @EliSawic
  35. 35. Thinking in Signals @EliSawic
  36. 36. What is a signal? @EliSawic
  37. 37. This screen is a signal @EliSawic
  38. 38. Represents events over time @EliSawic
  39. 39. Observing does not trigger side effects @EliSawic
  40. 40. No random access to events @EliSawic
  41. 41. Observe @EliSawic
  42. 42. @EliSawic
  43. 43. If you don't listen, it's gone @EliSawic
  44. 44. What is event? @EliSawic
  45. 45. Non-Terminating • Next @EliSawic
  46. 46. Terminating • Completed • Failed • Interrupted (Reactive Cocoa) @EliSawic
  47. 47. Location Service protocol LocationServiceDelegate { func locationService(locationService: LocationService, didUpdateLocation update: LocationUpdate) } class LocationService { var delegate: LocationServiceDelegate? private func onUpdate() { let update = LocationUpdate() delegate?.locationService(locationService: self, didUpdateLocation: update) } } @EliSawic
  48. 48. Location Service class LocationService { private func onUpdate() { let update = LocationUpdate() NotificationCenter.default.post(name: "LocationUpdate", object: update) } } @EliSawic
  49. 49. Location Service class LocationService { let locationUpdates: Signal<LocationUpdate, NoError> private let locationUpdatesSink: Signal<LocationUpdate, NoError>.Observer init() { (locationUpdates, locationUpdatesSink) = Signal<LocationUpdate, NoError>.pipe() } private func onUpdate() { let update = LocationUpdate() locationUpdatesSink.send(value: update) } } @EliSawic
  50. 50. Location Service class LocationService { let locationUpdates: Signal<LocationUpdate, NoError> private let locationUpdatesSink: Signal<LocationUpdate, NoError>.Observer init() { (locationUpdates, locationUpdatesSink) = Signal<LocationUpdate, NoError>.pipe() } private func onUpdate() { let update = LocationUpdate() locationUpdatesSink.send(value: update) } } @EliSawic
  51. 51. Location Service class LocationService { let locationUpdates: Signal<LocationUpdate, NoError> private let locationUpdatesSink: Signal<LocationUpdate, NoError>.Observer init() { (locationUpdates, locationUpdatesSink) = Signal<LocationUpdate, NoError>.pipe() } private func onUpdate() { let update = LocationUpdate() locationUpdatesSink.send(value: update) } } @EliSawic
  52. 52. Location Service class LocationService { let locationUpdates: Signal<LocationUpdate, NoError> private let locationUpdatesSink: Signal<LocationUpdate, NoError>.Observer init() { (locationUpdates, locationUpdatesSink) = Signal<LocationUpdate, NoError>.pipe() } private func onUpdate() { let update = LocationUpdate() locationUpdatesSink.send(value: update) } } @EliSawic
  53. 53. Observing locationUpdates.observeValues { update in // react to update } @EliSawic
  54. 54. Manipulating let homeUpdates = locationUpdates.filter { update in isHomeArea(update) } homeUpdates.observeValues { update in presentAlert("Welcome home") } @EliSawic
  55. 55. Manipulating let homeUpdates = locationUpdates.filter { update in isHomeArea(update) } homeUpdates.observeValues { update in presentAlert("Welcome home") } @EliSawic
  56. 56. Signal producer @EliSawic
  57. 57. Represents a tasks @EliSawic
  58. 58. Possible side effects @EliSawic
  59. 59. Does not start it's work if not asked @EliSawic
  60. 60. HTTP request func fetchUser() -> SignalProducer<User, FetchError> { return SignalProducer { observer, _ in // fetching user let user = User() observer.send(value: user) observer.sendCompleted() } } @EliSawic
  61. 61. HTTP request func fetchUser() -> SignalProducer<User, FetchError> { return SignalProducer { observer, _ in // fetching user let user = User() observer.send(value: user) observer.sendCompleted() } } @EliSawic
  62. 62. HTTP request func fetchUser() -> SignalProducer<User, FetchError> { return SignalProducer { observer, _ in // fetching user let user = User() observer.send(value: user) observer.sendCompleted() } } @EliSawic
  63. 63. HTTP request func fetchUser() -> SignalProducer<User, FetchError> { return SignalProducer { observer, _ in // fetching user let user = User() observer.send(value: user) observer.sendCompleted() } } @EliSawic
  64. 64. Work with presentation fetchUser().startWithResult { (result) in switch result { case .success(let user): //save user case .failure(let error): //show alert } } @EliSawic
  65. 65. Work with presentation fetchUser().startWithResult { (result) in switch result { case .success(let user): //save user case .failure(let error): //show alert } } @EliSawic
  66. 66. Work with presentation fetchUser().startWithResult { (result) in switch result { case .success(let user): //save user case .failure(let error): //show alert } } @EliSawic
  67. 67. Cold vs Hot @EliSawic
  68. 68. Signal SignalProducer presentation.observeValues presentation.startWithValues @EliSawic
  69. 69. Signal SignalProducer presentation.observeValues presentation.startWithValues emits values all the time waits for your request @EliSawic
  70. 70. // push based locationUpdates.observeValues { update in } // pull based fetchUser.startWithValues { user in } @EliSawic
  71. 71. // push based locationUpdates.subscribe { update in } // pull based fetchUser.subscribe { user in } @EliSawic
  72. 72. Properties @EliSawic
  73. 73. Mutable Property let firstSlide = Slide(number: 1) let slide = MutableProperty<Slide>(firstSlide) slide.producer.startWithValues { (text) in print(text) } slide.value = Slide(number: 2) @EliSawic
  74. 74. Mutable Property let firstSlide = Slide(number: 1) let slide = MutableProperty<Slide>(firstSlide) slide.producer.startWithValues { (text) in print(text) } slide.value = Slide(number: 2) @EliSawic
  75. 75. Mutable Property let firstSlide = Slide(number: 1) let slide = MutableProperty<Slide>(firstSlide) slide.producer.startWithValues { (text) in print(text) } slide.value = Slide(number: 2) @EliSawic
  76. 76. Mutable Property let firstSlide = Slide(number: 1) let slide = MutableProperty<Slide>(firstSlide) slide.producer.startWithValues { (text) in print(text) } slide.value = Slide(number: 2) @EliSawic
  77. 77. Mutable Property let firstSlide = Slide(number: 1) let slideContainer = MutableProperty<Slide>(firstSlide) slideContainer.producer.startWithValues { (text) in print(text) } slideContainer.value = Slide(number: 2) @EliSawic
  78. 78. Exposing properties let firstSlide = Slide(number: 1) let mutableSlideContainer = MutableProperty<Slide>(firstSlide) let readOnlySlide = Property(mutableSlideContainer) exposedSlide.producer.startWithValues { (text) in print(text) } @EliSawic
  79. 79. Exposing properties let firstSlide = Slide(number: 1) let mutableSlideContainer = MutableProperty<Slide>(firstSlide) let readOnlySlide = Property(mutableSlideContainer) exposedSlide.producer.startWithValues { (text) in print(text) } @EliSawic
  80. 80. Exposing properties let firstSlide = Slide(number: 1) let mutableSlideContainer = MutableProperty<Slide>(firstSlide) let readOnlySlide = Property(mutableSlideContainer) exposedSlide.producer.startWithValues { (text) in print(text) } @EliSawic
  81. 81. Bindings @EliSawic
  82. 82. Binding example let slideNumber = MutableProperty<Int>(0) let (slideSignal, _) = Signal<Slide, NoError>.pipe() slideNumber <~ slideSignal.map { return $0.number } @EliSawic
  83. 83. Binding example let slideNumber = MutableProperty<Int>(0) let (slideSignal, _) = Signal<Slide, NoError>.pipe() slideNumber <~ slideSignal.map { return $0.number } slideNumber signal 0 - @EliSawic
  84. 84. Binding example let slideNumber = MutableProperty<Int>(0) let (slideSignal, _) = Signal<Slide, NoError>.pipe() slideNumber <~ slideSignal.map { return $0.number } slideNumber signal 0 - 2 Slide(number: 2) @EliSawic
  85. 85. Reactive extensions let (slideSignal, _) = Signal<Slide, NoError>.pipe() label.reactive.text @EliSawic
  86. 86. Reactive extensions let (slideSignal, _) = Signal<Slide, NoError>.pipe() label.reactive.text <~ slideSignal.map { return "Slide: ($0.number)" } @EliSawic
  87. 87. Schedulers @EliSawic
  88. 88. Know where you are signal.observeValues { data in print("Performing UI updates") } @EliSawic
  89. 89. Know where you are signal.observe(on: QueueScheduler.main) .observeValues { data in print("Performing UI updates") } @EliSawic
  90. 90. Memory Management @EliSawic
  91. 91. Disposables @EliSawic
  92. 92. Free your memory let disposable = locationService.observeValues { update in // update location } disposable.dispose() @EliSawic
  93. 93. Lifetime let (lifetime, token) = Lifetime.make() locationService .take(during: lifetime) .observeValues { update in // update location } @EliSawic
  94. 94. Lifetime let (lifetime, token) = Lifetime.make() locationService .take(during: lifetime) .observeValues { update in // update location } @EliSawic
  95. 95. Lifetime let (lifetime, token) = Lifetime.make() locationService .take(during: lifetime) .observeValues { update in // update location } @EliSawic
  96. 96. Recap @EliSawic
  97. 97. Recap • Signals / SignalProducers @EliSawic
  98. 98. Recap • Signals / SignalProducers • Events @EliSawic
  99. 99. Recap • Signals / SignalProducers • Events • Properties @EliSawic
  100. 100. Recap • Signals / SignalProducers • Events • Properties • Bindings / Reactive extensions @EliSawic
  101. 101. Recap • Signals / SignalProducers • Events • Properties • Bindings / Reactive extensions • Schedulers @EliSawic
  102. 102. Recap • Signals / SignalProducers • Events • Properties • Bindings / Reactive extensions • Schedulers • Disposables / Lifetime @EliSawic
  103. 103. Example @EliSawic
  104. 104. @EliSawic
  105. 105. How does it work? @EliSawic
  106. 106. Is name valid? let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } @EliSawic
  107. 107. let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } nameSignal isValidNameSignal E false @EliSawic
  108. 108. let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } nameSignal isValidNameSignal E false El false @EliSawic
  109. 109. let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } nameSignal isValidNameSignal E false El false Eli true @EliSawic
  110. 110. let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } nameSignal isValidNameSignal E false El false Eli true Elia true Elias true @EliSawic
  111. 111. let isValidNameSignal = nameSignal.map { (name) -> Bool in return name.characters.count > 2 } nameSignal isValidNameSignal E false El false Eli true Elia true Elias true Eliasz true @EliSawic
  112. 112. Is surname valid? let isValidSurnameSignal = surnameSignal.map { (surname) -> Bool in return surname.characters.count > 2 } @EliSawic
  113. 113. Is mail valid? let isValidMailSignal = 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
  114. 114. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false @EliSawic
  115. 115. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false a@ false @EliSawic
  116. 116. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false a@ false a@b false @EliSawic
  117. 117. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false a@ false a@b false a@b. false @EliSawic
  118. 118. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false a@ false a@b false a@b. false a@b.c false @EliSawic
  119. 119. let isValidMailSignal = 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) } mailSignal isValidMailSignal a false a@ false a@b false a@b. false a@b.c false a@b.cd true @EliSawic
  120. 120. Combine Latest let formDataSignal = Signal.combineLatest( isValidNameSignal, isValidSurnameSignal, isValidMailSignal) @EliSawic
  121. 121. Combine Latest let formData = Signal.combineLatest( name, surname, mail) @EliSawic
  122. 122. let formData = Signal.combineLatest( name, surname, mail) formData name surname mail @EliSawic
  123. 123. let formData = Signal.combineLatest( name, surname, mail) formData name surname mail - true - - @EliSawic
  124. 124. let formData = Signal.combineLatest( name, surname, mail) formData name surname mail - true - - - true false - @EliSawic
  125. 125. let formData = Signal.combineLatest( name, surname, mail) formData name surname mail - true - - - true false - (true,false,true) true false true @EliSawic
  126. 126. let formData = Signal.combineLatest( name, surname, mail) formData name surname mail - true - - - true false - (true,false,true) true false true (true,true,true) true true true @EliSawic
  127. 127. Is form valid? let isValidFormSignal = formDataSignal.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail } @EliSawic
  128. 128. let isValidFormSignal = formDataSignal.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail } isValidForm formData false (true,false,true) @EliSawic
  129. 129. let isValidFormSignal = formDataSignal.map { (isValidName, isValidSurname, isValidMail) -> Bool in return isValidMail && isValidSurname && isValidMail } isValidForm formData false (true,false,true) true (true,true,true) @EliSawic
  130. 130. Update the state isValidFormSignal.observeValues { isValid in updateButtonWith(state: isValid) } @EliSawic
  131. 131. Binding let isValidForm = MutableProperty<Bool>(false) isValidForm <~ isFormValidSignal @EliSawic
  132. 132. Reactive everywhere! @EliSawic
  133. 133. Drawbacks @EliSawic
  134. 134. Know the drawbacks • Another layer for your app @EliSawic
  135. 135. Know the drawbacks • Another layer for your app • Learning curve @EliSawic
  136. 136. Know the drawbacks • Another layer for your app • Learning curve • Tricky debugging @EliSawic
  137. 137. Enjoy the pros @EliSawic
  138. 138. Enjoy the pros • Composable streams @EliSawic
  139. 139. Enjoy the pros • Composable streams • Easier asynchronous code @EliSawic
  140. 140. Enjoy the pros • Composable streams • Easier asynchronous code • Works great with MVVM @EliSawic
  141. 141. Stay Reactive @EliSawic
  142. 142. Stay Reactive sendCompleted() @EliSawic
  143. 143. @Elisawic www.eliaszsawicki.com @EliSawic

×