Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Functional Reactive Programming in Production

Using Functional Reactive Programming in Production

This talk will give you insights into how Functional Reactive Programming can help you build apps faster by making it more easy to implementing common patterns such as networking failures. I'll discuss the way we build apps like RTL XL and Buienradar at Triple and how we create projects which are familiar to work on for all developers in our team.

Combining ReactiveCocoa, Moya and a JSON parsing libraries creates new ways of networking in iOS apps built with Swift. By creating UI elements which can connect with these libraries you gain the ability to implement common patterns.

Antoine van der Lee

November 16, 2016
Tweet

More Decks by Antoine van der Lee

Other Decks in Programming

Transcript

  1. STREAMS + TRANSFORMATIONS + BINDINGS = FUNCTIONAL REACTIVE PROGRAMMING Functional

    Reactive Awesomeness With Swift, by Ash Furrow
 https://realm.io/news/altconf-ash-furrow-functional-reactive-swift/
  2. let scoreHome = MutableProperty<Int>(0) let scoreAway = MutableProperty<Int>(0) let scoreProducer

    = SignalProducer .combineLatest(scoreHome.producer, scoreAway.producer) // Create a stream
  3. let scoreHome = MutableProperty<Int>(0) let scoreAway = MutableProperty<Int>(0) let scoreProducer

    = SignalProducer .combineLatest(scoreHome.producer, scoreAway.producer) // Create a stream .map({ (scoreHome, scoreAway) -> String in return "Home \(scoreHome) - \(scoreAway) Away" // Transform the value })
  4. let scoreHome = MutableProperty<Int>(0) let scoreAway = MutableProperty<Int>(0) let scoreProducer

    = SignalProducer .combineLatest(scoreHome.producer, scoreAway.producer) // Create a stream .map({ (scoreHome, scoreAway) -> String in return "Home \(scoreHome) - \(scoreAway) Away" // Transform the value }) scoreboardLabel.reactive.text <~ scoreProducer // Bind to the label
  5. let scoreHome = MutableProperty<Int>(0) let scoreAway = MutableProperty<Int>(0) let scoreProducer

    = SignalProducer .combineLatest(scoreHome.producer, scoreAway.producer) // Create a stream .map({ (scoreHome, scoreAway) -> String in return "Home \(scoreHome) - \(scoreAway) Away" // Transform the value }) scoreboardLabel.reactive.text <~ scoreProducer // Bind to the label func homeScored(){ scoreHome.value += 1 } func awayScored(){ scoreAway.value += 1 } homeScored() // scoreboardLabel.text = “Home 1 - 0 Away” awayScored() // scoreboardLabel.text = “Home 1 - 1 Away” awayScored() // scoreboardLabel.text = “Home 1 - 2 Away”
  6. Languages Java: RxJava JavaScript: RxJS C#: Rx.NET C#(Unity): UniRx Scala:

    RxScala Clojure: RxClojure C++: RxCpp Lua: RxLua Ruby: Rx.rb Python: RxPY Groovy: RxGroovy JRuby: RxJRuby Kotlin: RxKotlin Swift: RxSwift PHP: RxPHP RxSwift vs ReactiveSwift
  7. let scoreHome = MutableProperty<Int>(0) let scoreAway = MutableProperty<Int>(0) ReactiveSwift RxSwift

    let scoreHome = Variable<Int>(0) let scoreAway = Variable<Int>(0)
  8. MANY PROJECTS > IMPLEMENTING PATTERNS MULTIPLE TIMES > A LOT

    OF SWITCHING BETWEEN PROJECTS FOR DEVS
  9. MANY PROJECTS BUILDING PATTERNS MULTIPLE TIMES > INTERNAL FRAMEWORKS A

    LOT OF SWITCHING BETWEEN PROJECTS > COMMON PROJECT SETUP
  10. > EASIER ADOPTION (ALSO FOR NEWBIES) < MANY PROJECTS BUILDING

    PATTERNS MULTIPLE TIMES > INTERNAL FRAMEWORKS A LOT OF SWITCHING BETWEEN PROJECTS > COMMON PROJECT SETUP
  11. INTERNAL FRAMEWORKS - TIFNetwork - TIFUIElements - TIFExtensions - TIFGoogleCast

    - TIFChatbot - ALDataRequestView - ALReactiveCocoaExtension Many more..
  12. INTERNAL FRAMEWORKS AS A SOLUTIONS FOR COMMON PATTERNS - Networking

    implementations - Networking failure - Calls signing - Empty datasets - Loading states
  13. - Networking implementations - Networking failure - Calls signing -

    Empty datasets - Loading states FRP TO GLUE THIS TOGETHER FRP INTERNAL FRAMEWORKS AS A SOLUTIONS FOR COMMON
  14. extension ExampleAPI: JSONMappableTargetType { var baseURL: URL { return URL(string:

    "https://httpbin.org")! } var path: String { switch self { case .getArray: return "/getarray" } } var method: Moya.Method { return .get } var parameters: [String: Any]? { return nil } var sampleData: Data { switch self { case .getArray: return stubbedResponseFromJSONFile("array_response") } } }
  15. RACProvider.request(token: ExampleAPI.getArray) // Create a stream .map(to: [GetResponse.self]) // Transform

    the value (response data) .on(value: { (array) in print(array.count) // Prints the count of the objects }, failed: { (error) in print(error) // Prints the error }) .start() // Start the data request
  16. CALL SIGNING extension BuienradarAPI : TIFRACAPI { public func request()

    -> SignalProducer<Moya.Response, Moya.Error> { return BRCallSigningManager.sharedManager.refreshSigningKeyIfNeeded().flatMap(.Latest) { (_) -> SignalProducer<Moya.Response, Moya.Error> in return TIFProvider.request(self) } } }
  17. CALL SIGNING extension BuienradarAPI : TIFRACAPI { public func request()

    -> SignalProducer<Moya.Response, Moya.Error> { return BRCallSigningManager.sharedManager.refreshSigningKeyIfNeeded().flatMap(.Latest) { (_) -> SignalProducer<Moya.Response, Moya.Error> in return TIFProvider.request(self) } } } CHECK IF SIGNING KEY IS STILL VALID BEFORE EACH REQUEST
  18. ALDATAREQUESTVIEW > 4 STATES • STREAM STARTS > SHOW LOADING

    VIEW • STREAM FAILS > SHOW FAILURE VIEW • STREAM SUCCESS & EMPTY > SHOW EMPTY VIEW • STREAM SUCCESS & DATA > HIDE
  19. dataRequestView = ALDataRequestView(forAutoLayout: ()) dataRequestView?.hidden = true dataRequestView?.dataSource = dataRequestViewDataSource

    view.addSubview(dataRequestView!) dataRequestView?.autoPinEdgesToSuperviewEdges() CONNECTING TO ALDATAREQUESTVIEW WITH FRP
  20. BuienradarAPI.WeatherVideoList() .request() // Create stream .map(to: [BRWeatherVideo]) // Transform .onNext

    { [weak self] (weatherVideos) -> () in self?.weatherVideos = weatherVideos self?.collectionView.reloadData() }.start() CONNECTING TO ALDATAREQUESTVIEW WITH FRP
  21. BuienradarAPI.WeatherVideoList() .request() // Create stream .map(to: [BRWeatherVideo]) // Transform .onNext

    { [weak self] (weatherVideos) -> () in self?.weatherVideos = weatherVideos self?.collectionView.reloadData() }.attachToDataRequestView(dataRequestView!) .start() CONNECTING TIFNETWORK TO ALDATAREQUESTVIEW
  22. public protocol ALDataRequestViewDataSource : class { func loadingViewForDataRequestView(dataRequestView: ALDataRequestView) ->

    UIView? func reloadViewControllerForDataRequestView(dataRequestView: ALDataRequestView) -> ALDataReloadType? func emptyViewForDataRequestView(dataRequestView: ALDataRequestView) -> UIView? func hideAnimationDurationForDataRequestView(dataRequestView: ALDataRequestView) -> NSTimeInterval func showAnimationDurationForDataRequestView(dataRequestView: ALDataRequestView) -> NSTimeInterval } ALDataRequestView, by A. van der Lee
 https://github.com/AvdLee/ALDataRequestView CONNECTING TIFNETWORK TO ALDATAREQUESTVIEW
  23. OTHER FRP IDEAS BASED ON .attachToSignalProducerReloader(appearReloader, reloadAction: reloaderReloadAction) override func

    viewDidAppear(animated: Bool) { super.viewDidAppear(animated) appearReloader.viewAppeared() }