Slide 1

Slide 1 text

⽇経 iOS プロジェクトの 
 マルチモジュール戦略 Go Takagi  2 02 3 / 09 / 13 After iOSDC LT Night 〜 ピクシブ×⽇経×タイミー 〜

Slide 2

Slide 2 text

Me ( Go Takagi ) ‣ID • shimastripe / shimastriper • bento.me/shimastripe ‣Work at • 株式会社 ⽇本経済新聞社 ‣ iOS エンジニア ‣ iOSDC NOC チーム ‣Like • Swift / ⾃動化 / 柴⽝ 2 電⼦版広報⽝デンシバ iOSDC 2 023 UIContentCon fi guration!!

Slide 3

Slide 3 text

iOSDC 20 23

Slide 4

Slide 4 text

スポンサーセッション⾯⽩かった!

Slide 5

Slide 5 text

SPM MultiModule 5 Application Monolith App Feature Feature Feature Usecase Common Usecase Common Repository Common ........... ........... ........... Swift Package Manager の各パッケージ App

Slide 6

Slide 6 text

既存プロジェクトの移⾏例の紹介 ‣ 必ずしもすぐマルチモジュール移⾏できる状態ではない...(ですよね) • 既存アーキテクチャとの兼ね合い • そもそも依存がきれいに分割されているか ‣ プロジェクトの状態を正確に把握する必要性がある • どういう効果を期待しているのか ‣ ⽇経がどういう技術選定をして移⾏しているか • ベースの基盤刷新も兼ねつつ、移⾏しています 6

Slide 7

Slide 7 text

⽇経電⼦版のアプリ 7

Slide 8

Slide 8 text

OSの機能を数多くサポート 8 アプリ内課⾦ WatchOSサポート Widgets Apple Pencil サポート ステッカー

Slide 9

Slide 9 text

KPIツリーと施策効果から⾒るアプリの役割 9 ݱ৔ͷࢪࡦͷࣄۀߩݙΛ໌֬ʹ ࣄۀՁ஋ (LTV) ܧଓ݄਺ F V ๚໰ස౓ ফඅهࣄຊ਺ ղ໿཈ࢭ Net ARPPU ηοτ঎඼୯Ձ ηοτ঎඼ߪೖ਺ ൢചख਺ྉ ೔ܦIDܾࡁൺ཰ ిࢠ൛ϓϥϯൺ཰ ՝ۚऀ਺ MAU ৽نདྷ๚ऀ ࠶དྷ๚ऀ ແྉτϥΠΞϧਃ ࠐ ॳճ՝ۚ཰ ՝ۚ཰ ిࢠ൛ͷࣄۀՁ஋ΛߴΊΔͨΊʹ͸ɺ՝ۚऀ਺Λ૿΍͢ɺ ՝ۚ୯ՁΛ্͛Δɺܧଓ݄਺Λ૿΍͢͜ͱ͕ඞཁ

Slide 10

Slide 10 text

成⻑し増加する画⾯‧Extension間のコード共有 ‣ マクロ分岐 • #ifdef IS_APP 等があちこちに...... ‣ TargetMembership • 依存関係の把握が必要‧ビルド時間も伸びる ‣ WatchApp との兼ね合い • UserDefaults や Keychain が繋がっていない ‣ ⾮同期処理‧DataBindingの管理 • App は RxSwift、AppExtension は Combine でそれぞれ重複実装していた • Concurrencyに移⾏したい........ 1 0

Slide 11

Slide 11 text

過去にEmbeddedFrameworkを作ったが ‣ EmbeddedFramework の性質‧プロジェクト構造の不理解 • GodCommonを作ってしまって結局戻した 1 1 https://speakerdeck.com/shimastripe/embedded-frameworkfalsesusume

Slide 12

Slide 12 text

改善したい、抱えている痛み ‣ 古いモジュールの改善‧それに伴う⼗分なドキュメントの整備 • メンテしづらい‧AppExtension の制約等の考慮 • テストを書きつつ現代ではいらないコードを消したい ‣ 依存⽅向をきちんと管理 • 無視した変更が⼊れづらい設計を保証する仕組みにしたい ‣ ⾮同期処理周り • RxSwift‧Combine のコードを Concurrency に移⾏したい ‣ ビルド時間の改善 • 画⾯数が多いため、ビルド時間が⻑くて開発効率に響いている 1 2

Slide 13

Slide 13 text

改善の変遷

Slide 14

Slide 14 text

AppExtension をまず整理 ‣ App の Subset であるため取り組みやすかった • Core になる仕組みを整理していく • 前処理Macroの整理‧共有している最⼩限のロジック群を把握 ‣ WatchOSの考慮 • Keychain‧UserDefaults など永続化周りを抽象化してAppの場合と住み分ける ‣ Combine ベースの⾮同期処理を Concurrency に置き換える • API‧DB などの Data 層の処理まで⼀旦 Concurrency 移⾏ • ⼀定サイズを移⾏後、Usecase や UI 層にも⼿を付け移⾏していく • App も RxSwift 向けに変換して利⽤する形で部分的に移⾏‧徐々に割合を増やしていく 1 4

Slide 15

Slide 15 text

Package.swift でレイヤー依存関係を定義 1 5 // MARK: - Utility // ֤ϨΠϠʔͷύοέʔδΛ༻ҙ͢Δศརؔ਺ protocol TargetProtocol { var category: String { get } var name: String { get } var hasTest: Bool { get } var testDependencies: [TargetDependency] { get } var allDeps: [TargetDependency] { get } /// default extension var dependency: TargetDependency { get } var targets: [Target] { get } }

Slide 16

Slide 16 text

レイヤー間の依存関係を制御 1 6 extension Data { static let orgUserRepository: Self = .init( name: "OrgUser", layer: .repository, dataDeps: [.keychainKit], domainDeps: [.model, .repositoryProtocol], hasTest: true ) }

Slide 17

Slide 17 text

MultiModule に移しつつコードを刷新する ‣ global な Extension の整理 • MultiModule に移して思わぬビルドエラーを踏んで把握 ‣ ⾮同期処理 • Concurrency で書き直して、RxSwift を持ち込まない • Combine は場合によって許容 ‣ DI • pointfree/swift-dependencies を採⽤して @Environment ベースで DI ‣ テスト • スコープを絞って整理が進むのできちんと書いていく 1 7

Slide 18

Slide 18 text

開発環境はどう変化してきたか 1 8 Project CLI ツール パッケージマネージャー CI Nikkei.xcodeproj Fastlane / Make fi le Carthage Bitrise (3年前)

Slide 19

Slide 19 text

開発環境はどう変化してきたか 1 9 Project CLI ツール パッケージマネージャー CI XcodeGen Mint SwiftPM Bitrise (Fastlane) (1年前)

Slide 20

Slide 20 text

開発環境はどう変化してきたか 2 0 Project CLI ツール パッケージマネージャー CI XcodeGen + 
 MultiModule + Swift Playgrounds Mint SwiftPM + Renovate Bitrise (Fastlane) (今)

Slide 21

Slide 21 text

Project 構成 (workspace) 2 1 設定ファイル (yml) Swift Playgrounds SPM MultiModule App (本体)

Slide 22

Slide 22 text

MultiModule + Swift Playgrounds へ移⾏中 2 2 Application Monolith App Feature Feature Feature Usecase Common Usecase Common Repository Common ........... ........... ........... Swift Package Manager の各パッケージ App

Slide 23

Slide 23 text

MultiModule + Swift Playgrounds へ移⾏中 2 3 Application Feature Feature Feature Usecase Common Usecase Common Repository Common ........... ........... ........... 保存記事App More... 紙⾯選択画⾯App 該当箇所のみビルドしてミニアプリ⽣成

Slide 24

Slide 24 text

Swift Playgrounds ミニアプリ ‣ アプリを画⾯単位で確認したい • 通常分割は⼤変 • エントリーポイントを変える仕組みもない • 設定ファイルもたくさん必要 ‣ Swift Playgrounds をミニハック • iPad (+ Mac) で Swift だけでアプリを作れる • 簡単なアプリしか作れない...? • マルチモジュールを Import してミニアプリを作れる • Package.swift の書き換えは想定されていない⽅法なことだけ注意 2 4 https://www.apple.com/jp/swift/playgrounds/ Demo.swiftpm ├── Package.swift └── Sources/MyApp/MyApp.swift エントリーポイントを容易に追加できて嬉しい

Slide 25

Slide 25 text

Xcode Preview との使い分けは悩み中 ‣ Preview 安定しない問題 • 特定のライブラリに依存して失敗するようになりやすい • 回避できるけど問題は"気づいたら"壊れているのが⾟い ‣ 現状のミニアプリの利点 • 安定してビルドして確認できる • 設定をユースケースに応じて動的に変えたりリッチなPreviewが作れて嬉しい • ミニアプリで有効な機能はApp側のDebug機能に移すことも 2 5

Slide 26

Slide 26 text

Stricted Concurrency Checking ‣ MultiModuleでもまだ ON にはしてない • 古いコードからの移⾏がつらくなるため • 度々⼿元でオンにしてみて様⼦⾒ • 特に UI レイヤーで ON にすると⼀気に⼤変な量のエラーに 😇 2 6

Slide 27

Slide 27 text

TestPlanを⾃動整備 ‣ TestTarget‧TestModule がどんどん増えていく • 内部の JSON はシンプル • 以下のコマンドでモジュールの⼀覧は取れる • xcrun --sdk macosx swift package --package-path MultiModule describe --type json • ビルド時に jq で TestPlan の⾃動アップデート 2 7 { "containerPath": "container:MultiModule", "identifier": "UseCase", "name": "UseCase" }

Slide 28

Slide 28 text

Xcode Template を整備 ‣ 標準ベースで書き直したモジュールに Template を⽤意 • APIRequest‧DB • swift-dependencies 周りの Template • ただ基本シンプルなものしか作れない • より複雑なTemplateが必要になったら Sourcery を検討する予定 2 8 https://github.com/krzysztofzablocki/Sourcery

Slide 29

Slide 29 text

DoCCを整備 (したい) ‣ いくつか問題点を抱えてまだ導⼊できず • plugin が platform: Mac 専⽤ • Xcode の generate docs でも • 特定のライブラリの docc ファイルを⾒てエラー....... • 複数 Scheme をまとめるドキュメント⽣成ができない 2 9 https://github.com/apple/swift-docc-plugin/issues/ 38 DoCC の運⽤ Tips ぜひ知りたいです!

Slide 30

Slide 30 text

まとめ ‣ Project の状態は様々 • MultiModuleへの移⾏は⼀般に時間がかかる • 痛みを認識し、そこから取り組んでいったほうが効果が実感できて良い • アプリが⼤きくなるとより継続性が⼤事 ‣ ⽇経のマルチモジュール移⾏→標準ベースのリアーキテクチャも兼ねる • OSの機能をリッチに活かしながらモダンな技術を利⽤できる環境 • OSの機能はアプリチームが⼀番詳しいので積極的に起案して作ってます • まだまだ⼀部だけで移⾏中。興味を持った⽅、弊社にぜひ声かけてください! 3 0