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
1.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
570
OSS貢献を気軽にしたい Let's Go Talk #1
yuyaabo
2
660
困ったときが学びどき.pdf
yuyaabo
0
660
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
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
yield再入門 #phpcon
o0h
PRO
0
680
PHP初心者セッション2026 〜生成AIでは見えない裏側を知る:今だからLAMPを通して仕組みを学ぶ〜
kashioka
0
610
AIエージェントで 変わるAndroid開発環境
takahirom
2
690
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
110
Haskell/Servantを通してWebミドルウェアを捉え直す
pizzacat83
1
600
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
140
Go言語とトイモデルで学ぶTransformerの気持ち / fukuokago23-transformer
monochromegane
0
130
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
270
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
310
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
340
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
230
Featured
See All Featured
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Scaling GitHub
holman
464
140k
Tell your own story through comics
letsgokoyo
1
1k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
1k
Navigating Weather and Climate Data
rabernat
0
390
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
320
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
Designing Experiences People Love
moore
143
24k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
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/ ͓ΘΓʹ