Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Tech fest

98 views

Published on

Functional Reactive Programming with Reactive Cocoa

Published in: Mobile
  • Be the first to comment

  • Be the first to like this

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

×