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
@cosmeアプリでのRx活用
Search
aboy
November 26, 2018
Programming
1.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
@cosmeアプリでのRx活用
15分ver
aboy
November 26, 2018
More Decks by aboy
See All by aboy
生成AIで日々のエラー調査を進めたい
yuyaabo
0
1k
みんなでエラー監視するSRE夕会の効果_ゆるSRE勉強会1
yuyaabo
1
740
Go 1.19.1 security fix net/url JoinPath
yuyaabo
1
560
HTTPステータスコードが意図した値にならないとき Let's Go Talk #2
yuyaabo
1
560
OSS貢献を気軽にしたい Let's Go Talk #1
yuyaabo
2
660
困ったときが学びどき.pdf
yuyaabo
0
650
Build dynamic iOS apps with the Create ML framework の要約
yuyaabo
0
1.3k
fastlaneベースでTravis CIからBitriseに移行しました
yuyaabo
0
1.9k
Mixpanelのすゝめ
yuyaabo
0
3k
Other Decks in Programming
See All in Programming
OS アップデート対応の取り組み方がもっと共有されてほしい
andpad
0
110
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
430
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
14
6.6k
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1.2k
Creating Composable Callables in Contemporary C++
rollbear
0
190
SLOをサービス品質の共通言語にするために 取り組んできたこと
wakana0222
0
310
Contextとはなにか
chiroruxx
1
390
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
140
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
190
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
290
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7.2k
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
820
Featured
See All Featured
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
Exploring anti-patterns in Rails
aemeredith
3
440
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
640
It's Worth the Effort
3n
188
29k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
460
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
We Have a Design System, Now What?
morganepeng
55
8.2k
Transcript
ˏcosmeΞϓϦͰͷRx׆༻ dip × istyle ߹ಉษڧձ#02 15
• Ѩอ༑ aboy • גࣜձࣾΞΠελΠϧͰɺˏcosmeΞϓϦˏcosmePROΞϓϦͷ։ൃΛ͍ͯ͠·͢ • iOSΞϓϦͱɺΞϓϦ༻APIήʔτΣΠΛ୲தͰ͢ • ೫ࡔ46͕͖ͳͷͰɺόΠτϧCMʹ͍ͭݩؾΛ͍͍͍ͨͩͯ·͢ɻdip͞ΜͱͯૉΒ͠ ͍ͱࢥ͍ͬͯ·͢ɻ͍ͭ͋Γ͕ͱ͏͍͟͝·͢ɻ
ࣗݾհ
ࠓ͢͜ͱ • ˏcosmeΞϓϦͰRxΛͬͯ։ൃ͍ͯ͠·͢ɻRxͷ׆༻ྫΛɺαϯϓϧίʔυΛަ͑ͯհ͠·͢ • εϥΠυͷαϯϓϧίʔυSwift(RxSwift/RxCocoa)Ͱ͢ɻઆ໌༻ͳͷͰɺʮ͋ΕΓͳ͍ Αʂʯͱ͍͏͕͍͔ͭͭ͋͘Γ·͢ɻΤϥʔϋϯυϦϯάͳ͍Ͱ͢ɻ ͞ͳ͍͜ͱ • ObservableͷHot/ColdError,CompletedͳͲɺRxͷৄ͍͠આ໌
Rxͱʁ
Rxͱʁ • Reactive Extensionsͷ͜ͱͰ͢ • Reactive ExtensionsͱɺϚΠΫϩιϑτ͔Β·ͬͨɺReactive ProgrammingΛ࣮ݱ͢Δ ͨΊͷσβΠϯɺͦͷ࣮ϥΠϒϥϦͷ͜ͱͰ͢ •
Reactive Programmingͱɺͬ͘͟Γݴ͏ͱprint(b)͕4Λग़ྗ͢ΔΑ͏ͳύϥμΠϜͰ͢ • ˏcosmeΞϓϦͰɺRxSwift(iOS)ɺRxJava(Android)Λ࣮ͬͯݱ͍ͯ͠·͢ a = 1 b = 2 * a a = 2 print(b)
Rxͱʁ • RxΦϒβʔόʔύλʔϯΛ༻͍࣮ͯ͞Ε͍ͯ·͢ • RxͰग़ͯ͘Δجຊతͳ֓೦Λ3ͭͬ͘͟Γઆ໌͠·͢ • ObservableͱɺߪಡՄೳͰ͋ΓɺΠϕϯτΛྲྀ͢ͷͰ͢ • OperatorsͱɺObservableΛมԽͤ͞ΔԋࢉࢠͷΑ͏ͳͷͰ͢ •
Subscribe(ߪಡ)ͱɺObservable͔ΒྲྀΕͯ͘ΔΠϕϯτΛࢹ͢Δ͜ͱͰ͢
Rxͱʁ let a = BehaviorSubject<Int>(value: 1) let b: Observable<Float> =
a .map { 2 * $0 } .map { Float($0) } b.subscribe(onNext: { (value: Float) in print(value) }) a.onNext(2) // 2.0 // 4.0 Observable Operators Subscribe ※1 ※1: BehaviorSubjectݫີʹObservableͰ͋ΓObserverͰ͢
ˏcosmeΞϓϦ × Rx • RxͷσʔλόΠϯσΟϯάػߏɺඇಉظॲཧͱͷ૬ੑͷྑ͞ΛϝϦοτʹײ͡ɺ1લͷˏcosme ΞϓϦϦχϡʔΞϧͷλΠϛϯάͰRxΛಋೖ͠·ͨ͠
ˏcosmeΞϓϦͰͷ׆༻ྫ
• ձһొը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷΈ߹Θͤ ׆༻ྫ
• ձһొը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷΈ߹Θͤ ׆༻ྫ
• ϝϧΞυͱύεϫʔυΛೖྗͯ͠ձһొ ͕Ͱ͖Δ • ϝϧΞυͱύεϫʔυʹͦΕͧΕόϦσʔ γϣϯ͕͋Δ • ྆ํOKͩͬͨΒձһొϘλϯ͕༗ޮԽ • όϦσʔγϣϯ݁ՌʹԠͯ͡ΤϥʔςΩε
τΛදࣔ ׆༻ྫɿձһొը໘ͷσʔλόΠϯσΟϯά
class SignUpViewModel { let password = Variable<String>("") } class SignUpViewController:
UITableViewController { @IBOutlet weak var passwordTextField: UITextField! private var viewModel: SignUpViewModel! override func viewDidLoad() { super.viewDidLoad() viewModel.password.asObservable() .bind(to: passwordTextField.rx.text) passwordTextField.rx.text.orEmpty .bind(to: viewModel.password) } } UI -> ViewModelͷόΠϯσΟϯά ViewModel -> UIͷόΠϯσΟϯά Input/Output݉ͷObservable ձһొը໘ɿجຊతͳσʔλόΠϯσΟϯά ※1 ※1: VariableRxSwiftಠࣗͷͷͰɺBehaviorSubjectͷϥούʔͰ͢ɻݱࡏdeplicatedͰ͢ɻ
class SignUpViewModel { private var isEmailValid: Observable<Bool> private var isPasswordLengthValid:
Observable<Bool> private var isPasswordCharacterValid: Observable<Bool> private var isPasswordValid: Observable<Bool> let canSignUp: Observable<Bool> init() { isEmailValid = email.asObservable() .map { (str: String) -> Bool in ϝϧΞυόϦσʔγϣϯ(str) } isPasswordCharacterValid = password.asObservable() .map { ύεϫʔυόϦσʔγϣϯ($0) } isPasswordLengthValid = password.asObservable() .map { 4 <= $0.count && $0.count <= 12 } isPasswordValid = Observable .combineLatest( isPasswordLengthValid, isPasswordCharacterValid ) { $0 && $1 } canSignUp = Observable .combineLatest(isEmailValid, isPasswordValid) { $0 && $1 } } } combineLatestෳͷΞΠςϜͷ࠷৽ͷ݁ՌΛ݁߹͠ɺ ධՁؔʹج͍ͮͯมͯ͠ฦ͢ΦϖϨʔλʔ mapΞΠςϜΛม͢ΔΦϖϨʔλʔ Stringܕ -> Boolܕͷม (Bool, Bool)ܕ -> Boolܕͷม ձһొը໘ɿձһొϘλϯͷ༗ޮԽ
class SignUpViewController: UIViewController { @IBOutlet private weak var signUpButton: UIButton!
private let viewModel = SignUpViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.canSignUp .bind(to: signUpButton.rx.isEnabled) } } ViewModel->UIͷόΠϯσΟϯά ೖྗ͞Εͨσʔλʹج͍ͮͨ ొϘλϯͷ༗ޮ/ඇ༗ޮԽ͕࣮ݱͰ͖Δ ձһొը໘ɿձһొϘλϯͷ༗ޮԽ
class SignUpViewModel { private var isPasswordLengthValid: Observable<Bool> private var isPasswordCharacterValid:
Observable<Bool> let passwordInvalidText: Observable<String> init() { isPasswordCharacterValid = password.asObservable() .map { ύεϫʔυόϦσʔγϣϯ($0) } isPasswordLengthValid = password.asObservable() .map { 4 <= $0.count && $0.count <= 12 } passwordInvalidText = Observable .combineLatest(isPasswordCharacterValid, isPasswordLengthValid) .map({ (charValid: Bool, lenValid: Bool) -> String in if !charValid { return "ύεϫʔυʹ༻Ͱ͖ͳ͍จࣈؚ͕·Ε͍ͯ·͢ɻ" } else if !lenValid { return "ύεϫʔυ4จࣈҎ্ɺ12จࣈҎͰೖྗ͍ͯͩ͘͠͞ɻ" } return "" }) } } όϦσʔγϣϯ݁ՌʹԠͨ͡ςΩετΛฦ͢ ձһొը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ
class SignUpViewController: UIViewController { @IBOutlet private weak var passwordInvalidLabel: UILabel!
private let viewModel = SignUpViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.isPasswordInvalidLabelHidden .bind(to: passwordInvalidLabel.rx.isHidden) viewModel.passwordInvalidText .bind(to: passwordInvalidLabel.rx.text) } } ձһొը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ Model->UIͷόΠϯσΟϯά
• ձһొը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷΈ߹Θͤ ׆༻ྫ
• Ұఆ࣌ؒ͝ͱʹAPIܦ༝Ͱσʔλऔಘ • εϨουͷߋ৽͕͋ΕϦετ(UI)Λߋ৽ ׆༻ྫɿϝοηʔδεϨουϦετͷϙʔϦϯά
class ThreadsViewModel { private let timer: Observable<Int> let polling: Observable<Thread>
init(repo: ThreadRepositoryProtocol = ThreadRepository()) { timer = Observable<Int> .interval(10.0, scheduler: MainScheduler.instance) polling = timer .flatMapFirst({ (_) -> Observable<[Thread]> in return repo.fetchThreads() }) .filter { ߋ৽͕͋Δ͔($0) } } } ϝοηʔδεϨουϦετͷϙʔϦϯά intervalΦϖϨʔλʔ ҰఆִؒͰΠϕϯτΛൃߦ flatMapFirstΦϖϨʔλʔ ม & ॲཧதແࢹ filterΦϖϨʔλʔ trueͳΒΠϕϯτΛૹΔ
• ձһొը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظAPI௨৴ॲཧͷΈ߹Θͤ ׆༻ྫ
• ͻͱͭͷը໘ʹෳηΫγϣϯ(ෳछྨͷίϯςϯπ) Λදࣔ • ηΫγϣϯऔಘͰ͖ͨͷ͔Βදࣔ • ηΫγϣϯ1ɺෳͷAPI͔ΒෳͷσʔλΛऔಘͯ͠ දࣔɻAPIͷ݁ՌΛશͯͭ ׆༻ྫɿඇಉظAPI௨৴ॲཧͷΈ߹Θͤ ηΫγϣϯ1
ηΫγϣϯ2 ηΫγϣϯ3
class ViewModel { private let cosme1: Observable<Cosme> private let cosme2:
Observable<Cosme> private let cosme3: Observable<Cosme> let section1: Observable<Void> init() { section1 = Observable .zip(cosme1, cosme2, cosme3) .map { มॲཧ } } } ඇಉظAPI௨৴ॲཧͷΈ߹ΘͤɿηΫγϣϯ̍ͷ࣮ zipͰඞཁͳObservableΛ·ͱΊΔ Πϕϯτͪ߹ΘͤΔ
class ViewModel { private let cosme1: Observable<Cosme> private let cosme2:
Observable<Cosme> private let cosme3: Observable<Cosme> let section1: Observable<Section1> let sectionList: Observable<[Section]> init() { section1 = Observable .zip(cosme1, cosme2, cosme3) .map { มॲཧ } sectionList = Observable .merge(section1, section2, section3) .map { มॲཧ } } } ඇಉظAPI௨৴ॲཧͷΈ߹ΘͤɿऔಘͰ͖ͨηΫγϣϯ͔Βදࣔ mergeͰඞཁͳObservableΛ·ͱΊΔ Πϕϯτͪ߹Θͤ͠ͳ͍ zipͰඞཁͳObservableΛ·ͱΊΔ Πϕϯτͪ߹ΘͤΔ
class ViewController: UIViewController { @IBOutlet private weak var tableView: UITableView!
private let viewModel = ViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.sectionList.subscribe(onNext: { [weak self] (_) in self?.tableView.reloadData() }) } } ඇಉظAPI௨৴ॲཧͷΈ߹ΘͤɿऔಘͰ͖ͨηΫγϣϯ͔Βදࣔ ηΫγϣϯͷϦετ͕ߋ৽͞ΕΔͨͼʹUIΛߋ৽
• ˏcosmeΞϓϦͰͷRx׆༻ྫΛհ͠·ͨ͠ɻσʔλόΠϯσΟϯάػߏɺObservableΛΈ߹Θͤ ͯඇಉظॲཧΛॻ͚Δັྗతʹײ͍ͯ͡·͢ɻجຊతʹΞϓϦέʔγϣϯશମͰRx͕ΘΕ͍ͯ·͢ • RxݴޠʹΑΒͳ͍֓೦ͳͷͰɺ࣮ϥΠϒϥϦؒͷࠩҟΛআ͚ɺiOS/AndroidΤϯδχΞؒͰͷڞ ௨ݴޠͱͯ͠ػೳ͢ΔϝϦοτ͋Γ·͢ • σʔλͷ͍ʹ͘͞ՄಡੑɺֶशʹࢿͰ͖Δ͔ɺͳͲͷ؍ͰɺνʔϜ։ൃͰ࠾༻͢Δ͔Ͳ͏͔ͦ ͷ࣌ͦͷ࣌ʹΑͬͯݟۃΊΔඞཁ͕͋Δ͔ͱࢥ͍·͢ •
ωΠςΟϒΞϓϦք۾Ͱؔ৺ͷߴ͍ٕज़Ͱ͋Δͱࢥ͏ͷͰɺ͜Ε͔ΒΥον͍ͯ͘͠ͱ͍͍͔ ͱࢥ͍·͢ • ReactiveXͱ͍͏αΠτʹυΩϡϝϯτ֤ݴޠͷRxϥΠϒϥϦ͕·ͱ·͍ͬͯΔͷͰ͝ཡ͍ͩ͘͞ http://reactivex.io/ ͓ΘΓʹ