Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Reactive thinking in Swift
Search
Ryo Aoyama
December 01, 2016
Programming
1
1.9k
Reactive thinking in Swift
presented at CA.swift #01
01 DEC. 2016
Ryo Aoyama
December 01, 2016
Tweet
Share
More Decks by Ryo Aoyama
See All by Ryo Aoyama
Micro Modular Architecture with Bazel
ra1028
9
1.7k
DifferenceKit in Action
ra1028
2
1.6k
Starting A11Y in iOS
ra1028
0
4.9k
Diffing inside SwiftUI List
ra1028
2
590
Integrate Combine into legacy frameworks
ra1028
2
610
Plasma - gRPC streamを利用したリアルタイムなユーザー体験
ra1028
3
1.4k
VueFlux - Flux inspired state managements
ra1028
3
1.5k
Optimizing Swift Collection
ra1028
2
2.9k
FRESH!配信アプリで採用した事・しなかった事
ra1028
8
4.8k
Other Decks in Programming
See All in Programming
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
220
Ruby on cygwin 2025-02
fd0
0
140
GAEログのコスト削減
mot_techtalk
0
120
SpringBoot3.4の構造化ログ #kanjava
irof
2
990
SwiftUI Viewの責務分離
elmetal
PRO
1
230
メンテが命: PHPフレームワークのコンテナ化とアップグレード戦略
shunta27
0
110
CNCF Project の作者が考えている OSS の運営
utam0k
6
710
Amazon ECS とマイクロサービスから考えるシステム構成
hiyanger
2
550
『品質』という言葉が嫌いな理由
korimu
0
160
プログラミング言語学習のススメ / why-do-i-learn-programming-language
yashi8484
0
130
第3回関東Kaggler会_AtCoderはKaggleの役に立つ
chettub
3
1k
ASP. NET CoreにおけるWebAPIの最新情報
tomokusaba
0
370
Featured
See All Featured
The Language of Interfaces
destraynor
156
24k
Speed Design
sergeychernyshev
27
790
Building an army of robots
kneath
303
45k
Building Adaptive Systems
keathley
40
2.4k
Become a Pro
speakerdeck
PRO
26
5.1k
Designing for Performance
lara
604
68k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
The Cult of Friendly URLs
andyhume
78
6.2k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Building Flexible Design Systems
yeseniaperezcruz
328
38k
Transcript
Reactive thinking in Swift 2016/12/01 CA.swift Ryo Aoyama / ra1028
#ca_swift
None
ແྉͰߴը࣭ͳੜ์ૹಈըΛݟ์ ੜ์ૹ৴ϓϥοτϑΥʔϜαʔϏε
FRP Functional Reactive Programming
RxSwift RxCocoa ReactiveSwift ReactiveCocoa
ɾඇಉظσʔλϑϩʔΛநԽ͠ɺ ౷ҰతͳΠϯλʔϑΣʔεͰ ѻ͏͜ͱ͕Ͱ͖Δ ɾঢ়ଶཧΛݮΒͤΔ ɾએݴతͳهड़͕Ͱ͖Δ Why use…?
ɾDelegate methods ɾCallback blocks ɾNotifications ɾControl actions ɾResponder chain events
ɾKey-value observing (KVO) ɾFutures and promises
Event Stream Observable<T> Signal<T, E> SignalProducer<T, E>
Event Stream?
Event Stream = Water pipe
Event Observable Signal SignalProducer Observe
Transforming
Map Next(x) Next(x * 10) Eventͷ࣋ͭΛม͢Δ map
FlatMap Next(•) Next(▪) 2ͭͷΠϕϯτετϦʔϜ Λ1ͭʹฏୱԽ͢Δ flatMap
Filtering
Filter ݅ʹ߹க͢Δ͚ͩΛ ड͚औΔ filter Next(5)
Combining
Merge ෳͷΠϕϯτετϦʔϜ ͷΛҰຊʹͯ͠ड͚औΔ merge
Sample with RxSwift UISegmentedControl, UITextField ͦΕͧΕͷΠϕϯτͰAPIϦΫΤετΛߦ͍ɺ ͦͷϨεϙϯεʹԠͯ͡UITableViewΛදࣔߋ৽͢Δɻ
without RxSwift
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 } }
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) } } } }
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 } }
ɾMutableͳม(ঢ়ଶ) ɾεϨουཧ ɾॲཧͷϑϩʔ͕ෳࡶ ɾ֦ு͕͍͠ ɾControl Actions ɾCallback blocks ɾDelegate methods
with RxSwift
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) } }
ɾMutableͳม(ঢ়ଶ) ɾεϨουཧ ɾॲཧͷϑϩʔ͕ෳࡶ ɾ֦ு͕͍͠ ɾControl Actions ɾCallback blocks ɾDelegate methods
Hot & Cold
Hot Observable Signal ɾߪಡऀͷ༗ແʹؔΘΒͣΠϕϯτ͕ྲྀΕΔ ɾෳߪಡ͞ΕΔͱετϦʔϜ͕ذ͢Δ
Subscribe Subscribe Subscribe Subscribe Hot
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 ----- ग़ྗ ذͤͨ̎ͭ͞ͷߪಡͰ ͕ڞ༗͞Ε͍ͯΔࣄ͕Θ͔Δ
Observable SignalProducer Cold ɾߪಡ͢Δ·Ͱಈ࡞͠ͳ͍ ɾߪಡ͞Ε͚ͨͩετϦʔϜ͕࡞ΒΕΔ
Subscribe Subscribe Subscribe Subscribe Cold
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 ------ ग़ྗ ̎ͭͷߪಡͰผʑͷ ετϦʔϜʹͳ͍ͬͯΔ
ColdͰॏ͍ॲཧAPIϦΫΤετΛ͍ͯ͠Δͱ…
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
Cold -> Hot
Subscribe Subscribe Subscribe Subscribe Cold -> Hot
Subscribe Subscribe Subscribe Subscribe Cold -> Hot
// 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
Hotม͍ͯ͠ΕɺColdͰॏ͍ॲཧ͕ߦΘΕͯ…
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
·ͱΊ
Event Stream ઃܭͷΠϯλʔϑΣʔε͕౷ҰԽɻ ࿈ଓతͳΛͲ͏ѻ͏͔͚ͩΛએݴతʹهड़͠ɺ ετϦʔϜಉ࢜Λଓ͢Δɻ = ඞવతʹMutableͳঢ়ଶཧݮΔɻ ίʔυྔͷݮ͕࠷େͷརͰͳ͍ɻ
Hot & Cold Hot or ColdͰετϦʔϜଓͷ͞Εํ͕ҟͳΔɻ ҙࣝ͠ͳ͍ͱϦιʔεͷແବɾҙਤ͠ͳ͍ڍಈɾόά ͷՄೳੑ͕͋Δɻ RxSwiftͰ҉తɻ ReactiveSwiftͰ໌ࣔతɻ
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠