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
20分でわかるセキュアAPI
nwiizo
2
310
2027年のMetricKit
kantacky
0
100
FORENSIA: ローカルLLMフォレンジックハーネス
sumeshi
2
420
tamachi.go 誕生の裏側
rymiyamoto
1
110
Apache Icebergインフラストラクチャ:ストレージ・カタログ・エンジンの選択肢とClouderaプラットフォームでの実装
tsugiyama
0
120
なぜ Temporal の大小比較には compare しかないのか / Why Does Temporal Only Have compare() for Comparisons
kazukihayase
1
170
ハーレムエンジニアリング
kazuma777777
0
190
老害フォレンジッカーはAI羊の夢を見るか?
tadmaddad
0
350
エンドユーザー視点で見る SansanのMeraki活用と内製自動化
sansantech
PRO
0
120
今こそ聞きたいソフトウェア設計 ドメイン駆動設計再入門
masuda220
PRO
17
7.6k
AI・HPC開発を支えるGPU環境の新しい選択肢 液冷GPUシステム「AquSys」の取り組み
gpuunite_official
0
100
社内の7割が使うデータ基盤を、 データチーム2人で回すためにやったこと
koh_yoshi
4
1.5k
Featured
See All Featured
Music & Morning Musume
bryan
47
7.3k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
SEO for Brand Visibility & Recognition
aleyda
0
4.7k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
870
Building the Perfect Custom Keyboard
takai
2
840
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
800
Optimizing for Happiness
mojombo
378
71k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
440
30 Presentation Tips
portentint
PRO
1
370
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