Open10

マルチモジュール勉強メモ~SPM~

さしもんさしもん

SPMも雰囲気で使っていたし
SPMを使ったマルチモジュール化についてもメモを残していく

さしもんさしもん
package.swift HostApplication

products.library.nameに書いた名前がHostApplication(メインターゲット)に追加するLibrary名になるみたい


で、Sources配下に置かれる青色の各フォルダが各targetになるみたい
だからここの名前とPackage.swift内で書くtarget.nameとtargets配列内の名前は一致させる必要がある
*library.nameに関しては単にHostApplication(メインターゲット)で追加するLibrary名になるからなんでもいいはず

さしもんさしもん

SPMでマルチモジュール対応するとき、たとえばここでいうモジュール「Target名」にFirebase/Firestoreのような外部ライブラリを入れるには
添付画像のようにPakcage.dependeciesにFirebaseを定義した上で導入したいtarget側でもdependencies側にも
dependencies: [.product(name: "FirebaseFirestore", package: "Firebase"),] のように定義する必要があるみたい
*エラー「
Showing Recent Messages
The package product 'FirebaseFirestore' requires minimum platform version 11.0 for the iOS platform, but this target supports 9.0」が表示されたので platforms: [.iOS(.v13)]でこのターゲットのサポートを13.0以上に引き上げた。何も設定しないとデフォルバリューが9.0になるのかもしれない

さしもんさしもん

マルチモジュールで管理してないときはDDDの文脈に沿ってDomain層にRepositoryのプロトコルも配置していたしそれで問題なかったけど
マルチモジュール化したときにはDataLayerのRepositoryImplがそれぞれのRepositoryプロトコルに準拠できなくてこまった

CookpadCoreは、すべてのモジュールから利用する抽象的なインターフェースの集合です。API通信やロギング、画面遷移などを行うためのインターフェースが定義されています。

クックパッドはどうやら全プロトコルを持つドメインを一つ作ってるっぽい?
https://logmi.jp/tech/articles/321186
このときの動画もここに載ってた
https://techconf.cookpad.com/2019/kohki_miki.html

またこれら2つのあとに書かれてる記事も勉強になる
https://techlife.cookpad.com/entry/2021/06/16/110000
https://techlife.cookpad.com/entry/2020/08/05/090000

さしもんさしもん

モジュール分離するときに気にする要素

モジュール分離には、分割の方法や粒度、移行プロセスなど、

他にも依存関係の解決とか

さしもんさしもん

モジュール分離の議論でしばしば話題に挙がるのが、アプリケーション全体をどのように分割し、再構成するかという点です。
我々のモジュール分割の大目的はビルド時間の削減にありました。
そのため、アプリ全体をビルドせずとも開発できるように、モジュール1つを単体起動できる構造を重視しています。
このような構成になっていることで、Feature Module単体のみを取り出し、個別にビルドすることが可能となりました。
この構成を生かした結果が、Sandboxアプリという動作確認用のミニアプリです。Feature Aの開発を行う場合は、CoreモジュールとFeature Aのビルドのみで動作確認ができるようになりました。

機能ごとにモジュールを分離することで機能ごとにビルドできるし、必然的にテストも機能単位で実行することができるから楽そう

さしもんさしもん

マルチモジュールで管理してないときはDDDの文脈に沿ってDomain層にRepositoryのプロトコルも配置していたしそれで問題なかったけど
マルチモジュール化したときにはDataLayerのRepositoryImplがそれぞれのRepositoryプロトコルに準拠できなくてこまった

この問題はマルチモジュールに戦略によるものではなくて
単純にこれは自分がオニオンアーキテクチャや依存関係逆転の法則を理解してなかったことが原因だった

ドメイン層をどこにも依存させない
リポジトリのインターフェースをドメイン層におき、データ層はそこに抽象に依存させればいいだけだった

参照画像1 参照画像2
参照: https://little-hands.hatenablog.com/entry/2017/10/11/075634
さしもんさしもん

https://github.com/tgrapperon/swift-dependencies-additions/pull/65

XCTDynamicOverlayをリンクさせる必要があるみたいだからSwiftPMについて基本的なこともあまり理解してなかったし上のバグを確認しつつ試してみた

パッケージの追加はGUIとPackage.swiftから2パターンがあって、GUIの方が楽だけど
今回みたいなリンクさせる・依存関係を修正する場合があるとGUIの方ではどうにもできないから
Package.swiftで追加した方が良さそう

その上で修正方法として自分はパッケージを用意してその中で依存関係を追加したけど、git submoduleでローカルにリポジトリを持ってきて問題のPackage.swift自体を編集した上で、プロジェクトの一部として組み込むのも良さそう

基礎知識

https://software.small-desk.com/development/2020/09/19/spmrealm-swift-package-for-package-depends-on-realm/

Package は、複数の Product を外部に提供することができますが、Package に依存すると定義しても、その Package に含まれる全ての Product に依存したことにはなりません。

つまり、denpendencies では、Package が依存する “Package” を定義できるのですが、それが全てのターゲットがその Package に依存していることを定義していることにはなりません。

target の dependencies では、targetが依存する target を定義できますが、依存する Product を書くこともできます。
Apple のドキュメントは、こちら。

ということで、一般化すると以下になります。

Package.swift 落とし穴
Target の依存関係は、以下の2段階で定義する必要がある。
dependencies のセクションで、依存する外部 Package を定義する
target の dependencies のセクションで、依存する Product を定義する
分かってしまえば、非常に理にかなった定義方法ですが、わかるまで時間かかりました。

説明は以上です。