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
AbemaTVにおけるiOSアーキテクチャの課題解決 / Solving problems i...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Yusuke Morishita
January 30, 2020
Technology
1.8k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
AbemaTVにおけるiOSアーキテクチャの課題解決 / Solving problems in AbemaTV iOS architecture
AbemaTV iOSの5年の運用の中で発生した設計課題とその解決方法について
Yusuke Morishita
January 30, 2020
Other Decks in Technology
See All in Technology
AWS Summit Japan 2026の振り返りと2027へ向けて / AWS Summit Japan 2026 Recap and Prospects for 2027
kaminashi
1
160
デジタル・デザイン構想 by Sayaka Ishizuka
y150saya
0
160
きのこカンファレンス2026_肩書きを外したとき私は誰か
yamasatimi
1
110
Zenoh on Zephyr on LiteX
takasehideki
2
160
AWS Summit の片隅で、体育座りしながらコミュニティがにぎわう理由を考えた
k_adachi_01
2
330
サイバーエージェントにおけるAI推進戦略と変革への取り組み
shotatsuge
0
640
“ID沼入口” - 基本とセキュリティから始める、考え続けるためのID管理技術勉強会 告知&イントロ
ritou
0
350
Microsoft のサポートとフィードバック総まとめ
murachiakira
PRO
0
130
AI時代における最適なQA組織の作り方
ymty
3
310
AWS PrivateLink × SCIM で実現する セキュアで運⽤負荷の低い Databricks 基盤の構築
tsuda7
0
120
「勝手に広まる」人気 AI エージェントを爆速で作ろう!(AWS Summit Japan 2026講演資料)
minorun365
PRO
10
2.8k
AIに障害切り分けを全部やってもらった。 。 。 。
estie
0
310
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Visualization
eitanlees
152
17k
Are puppies a ranking factor?
jonoalderson
1
3.7k
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Building the Perfect Custom Keyboard
takai
2
800
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.5k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
450
Mobile First: as difficult as doing things right
swwweet
225
10k
Transcript
"CFNB57ʹ͓͚Δ J04ΞʔΩςΫνϟͷ՝ղܾ .77. 6OJP 'MVY :VTVLF.PSJTIJUB!ZZTTLL "CFNB57։ൃہωΠςΟϒج൫
"HFOEB wϓϩμΫτഎܠ wઃܭͷมભ wݱࡏͷ՝
ϓϩμΫτഎܠ
ϓϩμΫτഎܠ w݄͔Β։࢝ͯ͠ݱࡏ wݱࡏ໊Ͱ։ൃ wϨϙδτϦ wJ04"QQ "1*ΫϥΠΞϯτ ϩάͷϞσ ϧʜFUD
ϓϩμΫτഎܠνʔϜਓ ࣌ؒ ਓ ্ཱͪ͛࣌
ઃܭͷมભ
ઃܭͷมભ wୈ̍ੈ'MVY wୈ̎ੈ.77. 'MVY wୈ̏ੈ.77. 6OJP 'MVY
ઃܭͷมભ$POUSJCVUJPOHSBQI ୈ̍ੈ ୈ̎ੈ ୈ̏ੈ
ઃܭͷมભ$POUSJCVUJPOHSBQI ୈ̍ੈ ୈ̎ੈ ୈ̏ੈ ॎԽɺϏσΦػೳ։࢝
ઃܭͷมભ$POUSJCVUJPOHSBQI ୈ̍ੈ ୈ̎ੈ ୈ̏ੈ ΞϓϦίΠϯɺ͛મػೳ։࢝
ઃܭͷมભνʔϜਓ ࣌ؒ ਓ ୈ̍ੈ ୈ̎ੈ ୈ̏ੈ
ୈ̍ੈ w࣌ظ wॳظϦϦʔε w.PUJWBUJPO wෳࡶͳঢ়ଶཧΛΘ͔Γ͘͢ཧ͍ͨ͠ wΫϥΠΞϯτؒ "OESPJEJ048FC ͰઃܭΛ౷Ұͨ͠ ͍
࠾༻'MVY 3Y4XJGU Facebook https://facebook.github.io/flux/docs/in-depth-overview/
ୈ̍ੈ'MVY 6*Πϕϯτ ঢ়ଶΛࢹͯ͠6*ө
ୈ̍ੈ"DUJPO func getElement() { dispatcher.isLoading.dispatch(true) APIClient.getElement() .do(onError: { [weak self]
error in self?.dispatcher.error(error) // Τϥʔ }) .do(onCompleted: { [weak self] in self?.dispatcher.isLoading.dispatch(false) // ྃ }) .subscribe(onNext: { [weak self] element in self?.dispatcher.element.dispatch(element) // ޭ }) .disposed(by: disposeBag) }
ୈ̍ੈ%JTQBUDIFS final class Dispatcher { static let shared = Dispatcher()
let element = DispatchSubject<Element>() let isLoading = DispatchSubject<Bool>() let error = DispatchSubject<Error>() }
ୈ̍ੈ4UPSF final class Store { static let shared = Store()
let isLoading: Property<Bool> let element: Property<Element> init(dispatcher: Dispatcher) { // dispatcher͔ΒྲྀΕ͖ͯͨΠϕϯτΛbind͢Δ } }
ୈ̍ੈ6* override func viewDidLoad() { super.viewDidLoad() // ActionʹΠϕϯτΛൃՐ Action.shared.getElement() //
ߋ৽͞Εͨঢ়ଶΛUIʹbind Store.shared.element.asObservable() .bind(to: titleLabel.rx.text) .diposed(by: disposeBag) }
ୈ̍ੈ'MVY 6*Πϕϯτ ঢ়ଶΛࢹͯ͠6*ө
ୈ̍ੈͷྑ͍ w7JFXؒͷґଘ͕ؔݮΔ w։ൃऀͷ࣮͕౷Ұ͞Ε͍͢ wσʔλͷྲྀΕ͕Θ͔Γ͍͢
ୈ̎ੈ w ࣌ظ w ݄dλςରԠͷ։ൃ։࢝ w ݄dϏσΦػೳͷ։ൃ։࢝
ୈ̍ੈͷ՝ w ػೳͷෳࡶԽʹΑΓɺ4UPSF6*ͷϥΠϑ αΠΫϧͷ߹ϩδοΫ͕7JFX$POUSPMMFS ʹ૿͑ͨ
ୈ̍ੈͷ՝ override func viewDidLoad() { super.viewDidLoad() // ঢ়ଶͷߋ৽·ͨλςϤίͷΓସ͑ΛϑοΫͯ͠tableViewΛϦϩʔυ Observable.combineLatest( Store.shared.repositories.asObservable(),
rx.traitCollectionDidChange.asObservable() ) .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] _ in self?.tableView.reloadData() }) .disposed(by: disposeBag) … }
࠾༻.77. 7JFX.PEFM .PEFM 7JFX 7JFX$POUSPMMFS %BUBCJOEJOH
ୈ̎ੈ7JFX4USFBN
ୈ̎ੈ7JFX4USFBN final class ViewStream { // Output let element: Property<Element>
let isLoading: Property<Bool> init( // Input viewDidLayout: Observable<Void>, traitCollectionDidChange: Observable<Void>, // Dependency action: Action = .shared, store: Store = .shared ) { // InputͷΠϕϯτΛͱʹOutputΛ߹͢Δ } }
ୈ̎ੈͷྑ͍ w7JFX$POUSPMMFSͷ࣮Λ6*Πϕϯτͷൖͱঢ়ଶ Λ6*ʹόΠϯυ͢ΔͷΈʹͰ͖ͨ wϓϨθϯςʔγϣϯϩδοΫΛ6*͔ΒͰ͖ɺ ୯ମςετָ͕ʹ͔͚Δ wάϩʔόϧͰอ࣋͢Δඞཁ͕ͳ͍ը໘ͷҰ࣌ঢ়ଶ Λ6*ͷϥΠϑαΠΫϧͰഁغͰ͖Δ
ઃܭͷมભνʔϜਓ ࣌ؒ ਓ ୈ̍ੈ ୈ̎ੈ ୈ̏ੈ
ୈ̎ੈͷ՝ w 7JFX4USFBNͷ࣮ํ๏͕࣮ऀʹΑͬͯ Β͖͕ͭ͋Δ
ୈ̎ੈͷ՝ class SearchViewStream { // Output let repositories: Property<[Repository]> private
let _repositories = BehaviorRelay<[Repository]>(value: []) private let _search = PublishRelay<String>() private let disposeBag = DisposeBag() init( // Dependency action: Action = .shared store: Store = .shared ) { // Input ⇨ Output Logic } // Input func search(_ text: string) { _search.accept(text) } }
ୈ̎ੈͷ՝ class SearchViewStream { // Output let repositories: Property<[Repository]> private
let _repositories = BehaviorRelay<[Repository]>(value: []) private let _search = PublishRelay<String>() private let disposeBag = DisposeBag() init( // Dependency action: Action = .shared store: Store = .shared ) { // Input ⇨ Output Logic } // Input func search(_ text: string) { _search.accept(text) } }
ୈ̎ੈͷ՝ class SearchViewStream { // Output let repositories: Property<[Repository]> private
let _repositories = BehaviorRelay<[Repository]>(value: []) private let disposeBag = DisposeBag() init( // Input search: Observable<String> // Dependency action: Action = .shared store: Store = .shared ) { // Input ⇨ Output Logic } }
ୈ̎ੈͷ՝ class SearchViewStream { // Input let search = PublishRelay<String>()
// Output let repositories: Property<[Repository]> private let _repositories = BehaviorRelay<[Repository]>(value: []) private let _search = PublishRelay<String>() private let disposeBag = DisposeBag() init( // Dependency action: Action = .shared store: Store = .shared ) { // Input ⇨ Output Logic } }
ߟҊ6OJP $SFBUFECZNBSUZTV[VLJ
ୈ̏ੈ w ࣌ظ w "CFNBίΠϯػೳͷ։ൃ w "CFNBαϙʔτ ͛મ ػೳͷ։ൃ
ୈ̏ੈ6OJP class SearchViewModel: UnioStream<SearchViewModel>, SearchViewModelType { struct Input: InputType {
let search = PublishRelay<String>() } typealias State = NoState struct Output: OutputType { let repositories: Observable<[Repository]> } struct Extra: ExtraType { let action: Action let store: Store } static func bind(from dependency: Dependency<Input, State, Extra>, disposeBag: DisposeBag) -> Output { // Dependency ⇨ Output Logic return Output(repositories: ${repositoriesΛฦ͢ม}) } }
ୈੈͷྑ͍ w7JFX.PEFMͷ࣮ͷ౷Ұ͕࣮ݱͰ͖ͨ w7JFX.PEFMͷϩδοΫͷྲྀΕ͕୯Ұํͳ ͷͰՄಡੑ্͕͕ͬͨ
ୈ̏ੈ6OJP
ݱࡏͷ՝
ݱࡏͷ՝ wਐԽ͍ͯ͘͠աఔͰલੈͷ࣮͕͍ͬͯΔ wୈ̍ੈͷ࣮ͰҰ෦'MVYͰ4UPSFUP4UPSF ͳ࣮ʹͳ͍ͬͯͯॲཧͷྲྀΕ͕ෳࡶԽ͍ͯ͠Δ wը໘ͷ୯ମىಈ͕Ͱ͖ͳ͍࣮ʹͳ͍ͬͯΔ w'MVYͷ"DUJPO͕3FTPVSDFࢀর͍ͯ͠Δ
·ͱΊ wઃܭͷมߋͷλΠϛϯάશػೳΛ৽͢ Δͱ͖େ͖ͳػೳ։ൃ͕ೖΔͱ͖ wॳظͷίϯηϓτ͔Βൃੜͨ͠՝ʹରͯ͠ ղܾ͢Δํ๏Λ৽ͨ͠ʹऔΓೖΕͯมԽ ͖ͯͨ͠
ࢀর w 'MVYXJUI3Y4XJGU w IUUQTTQFBLFSEFDLDPNEFLBUPUPSPqVYXJUISYTXJGU w .77. 'MVY w IUUQTTQFBLFSEFDLDPNNBSUZTV[VLJNWWNQMVTqVY
w "CFNBJ04"SDIJUFDUVSF w IUUQTTQFBLFSEFDLDPNUPJLJBCFNBJPTBSDIJUFDUVSF w 6OJP w IUUQTTQFBLFSEFDLDPNNBSUZTV[VLJNWWNGBMTFTIJ[IVBOHXPGVSVGSBNFXPSLXPLBJGBEBP SVTJUJNVEFCBSBUVLJHBBUVUBTIJ[IVBOHXPUPOHTVSV