今回はマルチモジュール開発の話。
最近のSwiftPackageを使ったマルチモジュール開発について調査しました。
はじめに
開発が大きくなったり複数人で進めていく場合、モジュール分割してないとよくわからない状態でも実装を進められてしまいます。最初はなんとなく動作しているのでうまく実装できているような気になりますが、その状態で進めると依存関係がどんどん複雑になり、不具合が多くなってしまう原因になります。
モジュールを分割することで、
- ある程度の実装方法を強制できる
- モジュール単位での依存関係が把握できる
- 関心のあるモジュールのみ把握すればいい 等
以上のメリットがあり、開発しやすくなります。
プロジェクトでモジュール分割
musicLineでは複数のプロジェクトを作成して、マルチモジュール開発を実現してました。
しかし、プロジェクト設定で依存関係やライブラリの種類を設定することが大変でした。
SwiftPackageでモジュール分割
以前よりもモジュール分割を細分化し、SwiftPackageでモジュール分割した結果です。
SwiftPackageを使用することで、フォルダ階層を分けるだけでモジュールを分割できました。
以前までのプロジェクト設定等の煩わしい作業がなくなり、スッキリしました。
ソースコード等の詳細はこちら
ポイント
1. フォルダを階層化したい
パッケージ設定で.target
によりターゲットを作成すると、デフォルトはSources
の直下のフォルダを参照しますが、path
ラベルよりフォルダのパスを設定できます。
例えば、CoreModel
の場合
.target(
name: "CoreModel",
path: "Sources/01.Core/CoreModel")
Package.swift
ファイルから相対パスで指定します。
2. モジュールにリソースを含める
モジュールにリソースを含める場合、resources
ラベルでリソースリストを指定します。
リソースは以下の2種類あり、モジュールフォルダからの相対パスを指定して作成します。
-
.process
フォルダ階層を除去して、Bundle直下にファイルをコピーする -
.copy
フォルダ階層そのままで、フォルダごとコピーする
例えば、Common
モジュール直下のdata
フォルダをリソースに追加したい場合、以下のように設定します。
.testTarget(
name: "CommonTests",
dependencies: ["Common"],
resources: [.process("data")]
)
また、プロジェクトからモジュールに移行するときに、参照するBundleに注意です。
// プロジェクトの場合
Bundle(for: type(of: self))
// モジュールの場合
Bundle.module
3. なぜかAssetsからリソースを取得できない
Assetsでリソースを登録しているのに、SwiftUIのColorやImageがリソースから取得できない状態になっていました。
原因は異なるBundleを参照しており、Assets内のリソースに指定したリソースが無いため、取得できていませんでした。特にどこのBundleを参照しているかを意識していなかったのですが、デフォルトではメイン(現在実行中のコードを含むBundle)を参照するようで、モジュールのBundleを参照することで解決しました。
// プロジェクトの場合
Color("color_name")
// モジュールの場合
Color("color_name", bundle: .module)
4. リリース時にPreview Contentフォルダを除外したい
プロジェクトみたいにDevelopment Assets
に登録して、リリース時にフォルダを除外するような挙動はできないようで困りました。
exclude
ラベルで一部のフォルダを除外できるようですが、デバッグ時には欲しいファイルなのでこれは使えず。。
最終的にはターゲットを新しく作成して、ライブラリを別にすることでこの問題を回避しました。でもこの方法だと、スキームが無駄に多くなってしまうことが気になるところです。
Previewする時のクラスはファイルに書き出さずに、Viewファイルの中でPreviewProvider
クラスと一緒に#if DEBUG
で定義した方がスマートかもしれません。
いい方法があればコメントください。。