Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Reactive thinking in Swift
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Ryo Aoyama
December 01, 2016
Programming
2.1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Reactive thinking in Swift
presented at CA.swift #01
01 DEC. 2016
Ryo Aoyama
December 01, 2016
More Decks by Ryo Aoyama
See All by Ryo Aoyama
Micro Modular Architecture with Bazel
ra1028
9
2k
DifferenceKit in Action
ra1028
2
1.8k
Starting A11Y in iOS
ra1028
0
5.4k
Diffing inside SwiftUI List
ra1028
2
730
Integrate Combine into legacy frameworks
ra1028
2
680
Plasma - gRPC streamを利用したリアルタイムなユーザー体験
ra1028
3
1.6k
VueFlux - Flux inspired state managements
ra1028
3
1.6k
Optimizing Swift Collection
ra1028
2
3.2k
FRESH!配信アプリで採用した事・しなかった事
ra1028
8
5.2k
Other Decks in Programming
See All in Programming
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.6k
Verilogで学ぶCPU自作入門
uyuki234
8
4.1k
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
940
Compiling a real-world CLI tool to native with Static Hermes
tmikov2023
0
100
難しいけど、読めた。- OSSの入口に立った話。
sts11142
0
140
カツオ、ご期待ください
suneo3476
0
160
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
120
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
570
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.8k
AIに “答え“を書かせるのを やめてみた
___akari11
0
110
Workers Cache を知る
syumai
0
350
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
1
350
Featured
See All Featured
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
350
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
260
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
340
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
560
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
The Limits of Empathy - UXLibs8
cassininazir
2
710
Rails Girls Zürich Keynote
gr2m
96
14k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
320
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Ͱ໌ࣔతɻ
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠