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

Reactive thinking in Swift

Ryo Aoyama
December 01, 2016

Reactive thinking in Swift

presented at CA.swift #01
01 DEC. 2016

Ryo Aoyama

December 01, 2016
Tweet

More Decks by Ryo Aoyama

Other Decks in Programming

Transcript

  1. final class NonRxViewController: UIViewController { @IBOutlet fileprivate weak var segmentedControl:

    UISegmentedControl! @IBOutlet fileprivate weak var textField: UITextField! @IBOutlet fileprivate weak var tableView: UITableView! fileprivate var items = [Item]() override func viewDidLoad() { super.viewDidLoad() tableView.register(SampleCell.self, forCellReuseIdentifier: "SampleCell") tableView.delegate = self tableView.dataSource = self } }
  2. private extension NonRxViewController { @IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {

    getItemsAndReload() } @IBAction func textFieldChanged(_ sender: UITextField) { getItemsAndReload() } func getItemsAndReload() { let text = textField.text ?? "" let index = segmentedControl.selectedSegmentIndex Api.getItems(segmentIndex: index, text: text) { [weak self] items in let reload = { self?.items = items self?.tableView.reloadData() } if Thread.isMainThread { reload() } else { DispatchQueue.main.async(execute: reload) } } } }
  3. extension NonRxViewController: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) ->

    Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleCell let item = items[indexPath.item] cell.configure(with: item) return cell } }
  4. final class RxViewController: UIViewController { @IBOutlet private weak var segmentedControl:

    UISegmentedControl! @IBOutlet private weak var textField: UITextField! @IBOutlet private weak var tableView: UITableView! private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() tableView.register(SampleCell.self, forCellReuseIdentifier: "SampleCell") Observable.combineLatest( segmentedControl.rx.value, textField.rx.text.orEmpty.distinctUntilChanged()) { ($0, $1) } .skip(1) .flatMap(Api.getItems) .asDriver(onErrorJustReturn: []) .drive(tableView.rx.items(cellIdentifier: "SampleCell", cellType: SampleCell.self)) { _, item, cell in cell.configure(with: item) } .addDisposableTo(disposeBag) } }
  5. Hot let subject = PublishSubject<Int>() let observer = subject.asObserver() let

    randomInt = { Int(arc4random_uniform(100)) } // PublishSubjectΛasObservable()͢ΔͱHot ObservableʹͳΔ let hot = subject.asObservable() hot.subscribe(onNext: { print("Hot 1: \($0)") }) hot.subscribe(onNext: { print("Hot 2: \($0)") }) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") ----- 1: 20 2: 20 ----- 1: 30 2: 30 ----- 1: 93 2: 93 ----- ग़ྗ ෼ذͤͨ̎ͭ͞ͷߪಡͰ ஋͕ڞ༗͞Ε͍ͯΔࣄ͕Θ͔Δ
  6. Cold // RxSwiftͰ͸Hot ObservableΛmap͢ΔͱCold ObservableʹͳΔ let cold = hot.map {

    $0 + randomInt() } cold.subscribe(onNext: { print("Cold 1: \($0)") }) cold.subscribe(onNext: { print("Cold 2: \($0)") }) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") ------ 1: 182 2: 117 ------ 1: 84 2: 139 ------ 1: 106 2: 64 ------ ग़ྗ ̎ͭͷߪಡͰผʑͷ ετϦʔϜʹͳ͍ͬͯΔ
  7. let cold = hot.map { i -> Int in sleep(1)

    // <- ॏ͍ॲཧ return i } cold.subscribe(onNext: { print("Cold 1: \($0), seconds: \(currentTimeSeconds())") }) cold.subscribe(onNext: { print("Cold 2: \($0), seconds: \(currentTimeSeconds())") }) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") ----------------------- Cold 1: 21, seconds: 49 Cold 2: 21, seconds: 50 ----------------------- Cold 1: 62, seconds: 51 Cold 2: 62, seconds: 52 ----------------------- Cold 1: 65, seconds: 53 Cold 2: 65, seconds: 54 ----------------------- ग़ྗ ੩తͳ஋Λฦ͍ͯͯ͠ݟ্͔͚஋͕ڞ༗Ͱ͖͍ͯͯ΋ɺ ॲཧ͸ߪಡ͍ͯ͠Δ͚ͩߦΘΕ͍ͯΔ Cold
  8. // Cold ObervableΛshareReplay͢Δ͜ͱͰHot ObservableʹͳΔ let coldToHot = cold.shareReplay(1) coldToHot.subscribe(onNext: {

    print("ColdToHot 1: \($0)") }) coldToHot.subscribe(onNext: { print("ColdToHot 2: \($0)") }) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") ----- 1: 32 2: 32 ----- 1: 54 2: 54 ----- 1: 144 2: 144 ----- ग़ྗ ஋Λڞ༗͢ΔΑ͏ʹͳͬͨ Cold -> Hot
  9. let cold = hot.map { i -> Int in sleep(1)

    // <- ॏ͍ॲཧ return i } .shareReplay(1) cold.subscribe(onNext: { print("Cold 1: \($0), seconds: \(currentTimeSeconds())") }) cold.subscribe(onNext: { print("Cold 2: \($0), seconds: \(currentTimeSeconds())") }) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") observer.onNext(randomInt()) print("-----") ---------------------- Cold 1: 6, seconds: 7 Cold 2: 6, seconds: 7 ---------------------- Cold 1: 74, seconds: 8 Cold 2: 74, seconds: 8 ---------------------- Cold 1: 77, seconds: 9 Cold 2: 77, seconds: 9 ---------------------- ग़ྗ ෳ਺ߪಡ͍ͯͯ͠΋ ॲཧ͕ߦΘΕΔͷ͸Ұճ͚ͩ Cold -> Hot