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
Yusuke Morishita
January 30, 2020
Technology
1
1.6k
AbemaTVにおけるiOSアーキテクチャの課題解決 / Solving problems in AbemaTV iOS architecture
AbemaTV iOSの5年の運用の中で発生した設計課題とその解決方法について
Yusuke Morishita
January 30, 2020
Tweet
Share
Other Decks in Technology
See All in Technology
MCP Documentation Server @AI Coding Meetup #1
yyoshiki41
2
2.6k
Devinで模索する AIファースト開発〜ゼロベースから始めるDevOpsの進化〜
potix2
PRO
5
2.4k
データベースで見る『家族アルバム みてね』の変遷 / The Evolution of Family Album Through the Lens of Databases
kohbis
4
1.1k
ソフトウェア開発現代史: "LeanとDevOpsの科学"の「科学」とは何か? - DORA Report 10年の変遷を追って - #DevOpsDaysTokyo
takabow
0
190
Amebaにおける Platform Engineeringの実践
kumorn5s
6
890
JPOUG Tech Talk #12 UNDO Tablespace Reintroduction
nori_shinoda
1
110
Cursor AgentによるパーソナルAIアシスタント育成入門―業務のプロンプト化・MCPの活用
os1ma
7
1.8k
DuckDB MCPサーバーを使ってAWSコストを分析させてみた / AWS cost analysis with DuckDB MCP server
masahirokawahara
0
410
自分の軸足を見つけろ
tsuemura
2
570
30 代子育て SRE が考える SRE ナレッジマネジメントの現在と将来
kworkdev
PRO
0
200
Re:VIEWで書いた「Compose で Android の edge-to-edge に対応する」をRoo Codeで発表資料にしてもらった
tomoya0x00
0
260
OPENLOGI Company Profile
hr01
0
62k
Featured
See All Featured
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
640
Rails Girls Zürich Keynote
gr2m
94
13k
Become a Pro
speakerdeck
PRO
27
5.3k
The Cost Of JavaScript in 2023
addyosmani
49
7.7k
Visualization
eitanlees
146
16k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.2k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
12k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.5k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
52
2.4k
Bash Introduction
62gerente
611
210k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
178
52k
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