Slide 1

Slide 1 text

動画配信サービス CLでの
 スケーラブルなアプリ設計
 
 2023/11/20
 CA.swift #18


Slide 2

Slide 2 text

# 経歴
 ・2019年4月 - 新卒入社
 ・2019年5月 - CL iOS
 
 # 趣味
 ・個人開発
 ・最近アプリでポーカーとかやってます
 ・etc…
 
 # その他
 ・個人開発でリリースしたアプリが
  Utilityカテゴリで30位になった
 自己紹介
 
 
 
 
 下村一将 / かず
 しもむら かずまさ
 
 github.com/s2mr
 x.com/_kzumu
 2

Slide 3

Slide 3 text

# 最近の活動
 自己紹介
 
 
 
 
 下村一将 / かず
 しもむら かずまさ
 
 github.com/s2mr
 x.com/_kzumu
 github.com/s2mr/L10nLint
 Give me star! 
 3

Slide 4

Slide 4 text

- CLの事業内容
 - アプリの機能などのドメインについて 
 - CLの開発体制について
 - 組織図
 - 使用している技術・アーキテクチャ解説
 - VueFluxやXcodeGen,マルチモジュール戦略などについて 
 - Widget開発に伴った設計の刷新 
 - CIについて
 - キャッシュ戦略や並列化について 
 - CLのこれから
 - 開発関連の課題
 アジェンダ
 4

Slide 5

Slide 5 text

CLの事業内容


Slide 6

Slide 6 text

CLのサービス概略
 動画配信サービス 6

Slide 7

Slide 7 text

包括的ファンサービス CLのサービス概略
 7

Slide 8

Slide 8 text

対応デバイス
 - iOS
 - Android
 - Web
 - TV(AirPlay, ChromeCast, AndroidTV)
 その他
 8

Slide 9

Slide 9 text

CLの開発体制


Slide 10

Slide 10 text

開発体制
 10

Slide 11

Slide 11 text

技術概要


Slide 12

Slide 12 text

Micro framework
 $ xcodegen dump --type graphviz | grep -v xcframework | grep -v Test | grep -- "->" | sed -e 's/\[style=dashed\]-/g' paste into https:-/play.d2lang.com/ 12

Slide 13

Slide 13 text

Micro framework
 大きく分けて以下の分類 
 - App
 - CyberLDH, UICatalog, Playground 
 - CLFoundation
 - CyberLDHCast、CyberLDH用の Adapter
 - Component
 - CastComponent
 - Component
 - 各FeatureComponent(後述) 
 - SharedComponent
 13 - Model
 - UI層でも使用するAPIレスポンスマッ プ用のモデル
 - Request
 - 各APIのリクエスト定義ファイル 
 - Proto
 - APIから届くpb.swiftのファイル 
 


Slide 14

Slide 14 text

・TDD(Test-Driven Development) ・パターンごとの UIを定義 ・APIなしでもUIを作れる ・PRでUIのDiffチェックをできる ・デザイナーも簡単に全 UIをチェック 差分検知・WebのViewer reg-viz/reg-suit iOSのViewer・Snapshotテスト playbook-ui/playbook-ios Visual Regression Test
 14

Slide 15

Slide 15 text

安全にローカライズを行うための仕組み
 github.com/s2mr/L10nLint
 15

Slide 16

Slide 16 text

- VueFlux ( github.com/ra1028/VueFlux )
 - 画面単位でStore, Action, Mutationを持つ、
 Fluxベースのアーキテクチャ
 - 現状CLの全ての画面はVueFluxベースで開発されている
 - VueFluxをDIしやすくするためにアレンジして利用している
 - Adapter層でUIとState層を接続
 - シンプルで扱いやすい
 - UIKitとの相性はいいが、SwiftUIとはいまいちマッチしない(後述)
 アーキテクチャ
 16

Slide 17

Slide 17 text

本体アプリとComponentの参照関係
 CyberLDH Component モジュール ViewController AdapterProtocol Adapter Store 実線矢印:参照の方向 破線矢印:依存の方向 クラス Repository Protocol Repository Snackbar Center Snackbar Center Protocol etc… 17 本体アプリ

Slide 18

Slide 18 text

◆ Componentが肥大化しやすい 
 
 😇原因
 Adapter, Stateは1画面に対して1つずつだが、 
 Viewは1画面に対して複数作成されて、増加スピードが著しいため 
 
 🌟対策
 FeatureごとにComponentを作成する運用を開始 
 👉 並列ビルドでビルド時間の削減や、ミニアプリを作る際の依存関係を減 らせるなどの恩恵が!
 
 💡方針
 既存の一枚岩となったComponentを各FeatureComponent, SharedComponentへ移行
 これまでの分割方法の課題
 Shared Component Component Mission Component Community Component 18

Slide 19

Slide 19 text

既存の画面はほとんどxibファイルで作成されており、ファイルを移動してコンパイルを通すだけでは 問題が。
 
 😇xibのModule名指定をxibごとに更新する必要がある 
 忘れた場合、ヒューマンエラーによるクラッシュ 
 
 🌟XibCheck
 1. SwiftSyntaxを使って各Frameworkに存在するクラスをリストアップ。 
 2. IBDecodableで各種xib, storyboard内のModule設定をチェック。 
 3. xibで設定されたModule名の参照が間違っていればGitHubのPR上にErrorを表示。 
 Componentを安全に移動するための仕組み
 いにしえのxibファイル 19

Slide 20

Slide 20 text

Widget機能のアーキテクチャ
 - TCAを選択しなかった理由
 - RootにReducerを持つので、画面単位で切り替えやすいアーキテクチャではない(アプリ 全体に依存が入る)
 - 作りにもよるが各画面に多くのTCA依存が発生し、PureなSwiftUIViewではなくなる 
 - 各Reducer は状態の更新とAPI呼び出しの責務を持つので肥大しやすい 
 - 依存関係が多くなる(11個が一緒についてくる) 
 - メンバーのキャッチアップコスト
 - SwiftUI側もまだ発展途中(Observable Macrosが出てきたり) 
 もし誤りがあれば教えてください󰢛 20

Slide 21

Slide 21 text

- HomeScreen(画面のライフサイクルなどを扱う層) 
 - Adapterを渡して、StateObjectとして扱う
 - HomeContentを表示して、クロージャとAdapterの接続やonAppearなどを管理 
 - HomeContent(Viewの実態)
 - HomeDataModel
 - Eventのクロージャを渡して、HomeScreen階層で扱う 
 - 👉 Adapterは使わないのでPreview時にはDataModelと空のクロージャを渡してあげるだけで良い 
 - HomeAdapter
 - 各種クラスのDIが可能
 - @PublishedでViewに公開するものはHomeStateのみ 
 - Stateの更新はFluxのようなActionを発行して行う 
 Widget機能のアーキテクチャ(検討中)
 21

Slide 22

Slide 22 text

Widget機能のアーキテクチャ
 22

Slide 23

Slide 23 text

Previewの為にTestDouble(モックデータ)をimportすることは 妥協 今後デバッグ時のみリンクできるようにしたい ScreenとContentを切り離すことで、 Preview時にModelを差し替えるだけで任意の UIを表示できる ように💡 23

Slide 24

Slide 24 text

24

Slide 25

Slide 25 text

・レスポンスのパースは 肥大化しやすい部分 
 ・必要に応じてUI用の Modelへ変換
 ・VueFluxの使用感が良 いので参考に
 ・throwされるSwift.Error はここで変換してアプリ 内で出し分け
 25

Slide 26

Slide 26 text

UIKitベースのアプリへの導入なので、
 最終的にはUIHostingControllerでラップする
 💡この設計で4画面くらい実装を終えていますが、 
  プレビューも活用できてかなり開発体験はいいです。(UIだけの実装で体感6倍の開発速度) 
 Widget機能のアーキテクチャ(検討中)
 26

Slide 27

Slide 27 text

CIについて


Slide 28

Slide 28 text

コードボリューム
 $ cloc . --vcs=git 5735 text files. 5194 unique files. -------------------------------------------------------------------------------- Language files blank comment code -------------------------------------------------------------------------------- Swift 4093 42240 12722 229366 XML 514 388 2 42163 JSON 410 0 0 30685 Markdown 14 1733 0 4192 YAML 35 99 11 1768 Bourne Shell 32 197 194 692 Text 50 80 0 567 Ruby 29 63 0 478 SVG 2 2 0 257 -------------------------------------------------------------------------------- SUM: 5194 44915 12995 310399 -------------------------------------------------------------------------------- CL 28

Slide 29

Slide 29 text

CI稼働時間 (CircleCI)
 - 可能な限りビルド済みバイナリを使 用することによりCI時間を削減
 - x86.medium.gen2 -> m1.medium.gen1 へ切り替えることにより65%も高速 化!
 - アプリの配布と各種テストを含め
 約13分程度で完了
 29

Slide 30

Slide 30 text

CLのこれから


Slide 31

Slide 31 text

- かなりリッチなWidget機能
 - iOS17+
 - かなり攻めれる
 - 技術検証の場に
 - AppIntentsでの構成可能なWidget作成 
 - SwiftUI
 - やることたくさん
 - CoreData, KMP
 - Xcode15に移行したい(CIの問題も最近ありましたね) 
 CLのこれから
 31

Slide 32

Slide 32 text

yatteiki
 # 開発効率アップ
 - SPM化
 - StoreKit2
 - TipKit(iOS17+)
 
 # 開発品質
 - 視聴面の実装複雑化への対応として、Adapter, Stateのテストを書く(like E2E) 
 32

Slide 33

Slide 33 text

- 大きめの施策リリース前や、急ぎでリリースする案件はお触り会をやったり
 - Developer Hours
 - KPT
 - WWDC振り返り
 - 使ってみたい新機能や今後入れられそうな機能をチーム内ですり合わせ
 
 
 独自の取り組み
 33

Slide 34

Slide 34 text

We are hiring!