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

Everyday Reactive (try! Swift Tokyo '17)

Everyday Reactive (try! Swift Tokyo '17)

This is my talk from try! Swift Tokyo exploring tips and tricks for determining when reactive programming can be a potent tool, as well as scenarios to avoid that might threaten code quality and performance. The talk's focus is on concepts in reactive programming, the code shows off different Swift reactive implementations.

https://www.tryswift.co/tokyo/

6190e163993110536deb0767f9f63fdb?s=128

Agnes Vasarhelyi

March 09, 2017
Tweet

Transcript

  1. EVERYDAY REACTIVE 毎⽇日リアクティブ Agnes Vasarhelyi @vasarhelyia
 ! Ustream (IBM Cloud

    Video)
  2. None
  3. "Reactive programming manages asynchronous data flows between sources of data

    and components that need to react to that data." MATT GALLAGHER, COCOAWITHLOVE.COM
  4. • Reduces state • Less, but more concise code •

    Easier to read
  5. IS THERE A FRAMEWORK FOR THAT? There are plenty.

  6. None
  7. WHEN DO I APPLY IT? Depends on complexity.

  8. WHERE DO I APPLY IT? • MVVM • async ops

  9. OBSERVATION

  10. let (signal, observer) = Signal<String, NoError>.pipe() signal.observeValues { animal in

    print( "value: \(animal)") } observer.sendNext(value: "") observer.sendNext(value: "") > value: > value: ReactiveSwift (RAC)
  11. LET'S REDUCE SOME STATE

  12. var loading: Bool var userLoggedIn: Bool var didShowAlert: Bool var

    loading = reactive(userLoggedIn) && reactive(didShowAlert) 23 STATE 22 STATE
  13. TRANSFORM / COMBINE

  14. let userLoggedIn = PublishSubject<Bool>() let didLoadContent = PublishSubject<Bool>() let loading

    = Observable.combineLatest(userLoggedIn, didLoadContent) { !($0 && $1) } loading.subscribe(onNext: { print("loading: \($0)") }) userLoggedIn.onNext(false) didLoadContent.onNext(false) userLoggedIn.onNext(true) didLoadContent.onNext(true) > loading: true > loading: true > loading: false RxSwift
  15. SIMPLIFY / UNIFY ASYNC OPERATIONS

  16. let catButton = UIButton(title: "") let url = URL(string: "http://catfacts-api.appspot.com/api/facts?

    number=1") let catFact = URLSession.shared.rx.json(url:url!) .map( { return "\(parsedCatFact($0))" }) Observable.combineLatest(catButton.rx.tap, catFact) { c, fact in "\(c) \(fact)" }.subscribe(onNext: { print("\($0)") }) > : Cats can be right-pawed or left-pawed. > : In 1987 cats overtook dogs as the number one pet in America. RxSwift
  17. TRADE-OFFS, COMPLICATIONS

  18. 1. DEBUGGING

  19. HOW TO HELP IT?

  20. func logAnimals() { let animal = MutableProperty<String>("") let animalStream =

    animal.producer.logEvents(identifier: "") animalStream.start() animal.value = "" } [] starting fileName: (...)/A.swift, functionName: logAnimals(), lineNumber: 16 [] value fileName: (...)/A.swift, functionName: logAnimals(), lineNumber: 16 [] started fileName: (...)/A.swift, functionName: logAnimals(), lineNumber: 16 [] value fileName: (...)/A.swift, functionName: logAnimals(), lineNumber: 16 ... ReactiveSwift (RAC)
  21. 2. "FANCY KVO"

  22. viewModel.title.observeNext { [weak self] v in self?.titleLabel.text = v self?.refreshButton.enabled

    = true self?.canRefresh = true }) func update() { if (!self.canRefresh) return; ... } Bond
  23. viewModel.title.bind(to: titleLabel) Bond

  24. viewModel.title.map { n -> Bool in return n != nil

    }.bind(to: refreshButton.reactive.isEnabled) Bond
  25. viewModel.title.bind(to: titleLabel) viewModel.title .bidirectionalBind(to: titleTextField.reactive.bnd_text) Bond

  26. 3. INJECT SIDE EFFECTS CAREFULLY

  27. let catFact = URLSession.shared.rx.json(url:url!) .map( { return "\(parsedCatFact($0))" }) .do(onNext:

    { _ in print("EFFECT") }) catFact.subscribe(onNext: { print("1. : \($0)") }) catFact.subscribe(onNext: { print("2. : \($0)") }) > EFFECT > 1. : Most cats adore sardines. > EFFECT > 2. : Cats take between 20-40 breaths per minute. RxSwift
  28. ... .do(onNext: { fact in Analytics.trackEvent("cat fact generated: \(fact)") })

  29. 4. AVOID CHAOS

  30. Thread 1 eraseButton.reactive.tap.onNext( { stateManager.items.remove($0) }) Thread 2 apiResponse.onNext (

    { stateManager.items.append($0) }) eraseButton.reactive.tap.onNext( { stateManager.removeItem($0) }).onScheduler(MainScheduler) apiResponse.onNext ( { stateManager.appendItem($0) }).onScheduler(BackgroundScheduler) immutability + schedulers =
  31. None
  32. ... .ignoreNil() .flatMapLatest { $0.unwrap } .collect() .first() .filter(include: {

    s -> Bool in return s.characters.count > 3 }) .merge(with: offlineSignal.distinct()) .combineLatest(with: viewModel.title.map { t -> Bool in return t != nil }).bind(to: refreshButton.reactive.isEnabled)
  33. NO MORE 
 IMPERATIVE?

  34. DESIGNING APIS • don't expose reactive, • support it

  35. THANKS
 @vasarhelyia

  36. RESOURCES https://github.com/ReactiveKit/Bond https://github.com/ReactiveX/RxSwift https://github.com/ReactiveCocoa/ReactiveCocoa https://github.com/mattgallagher/CwlSignal https://www.cocoawithlove.com/blog/reactive-programming-what-and-why.html https://www.youtube.com/watch?v=7AqXBuJOJkY https://speakerdeck.com/joshaber/better-code-for-a-better-world Photos from

    unsplash.com