iOS開発でマルチモジュール実践
この記事では、iOS開発でマルチモジュールを実践します。
モジュールとは
モジュールとは、ソフトウェアのクラス群を意味のあるまとまりに整理することで、構成要素同士の相互依存性を低くする存在・概念です[1]。モジュールとしてまとめることにより、開発効率・メンテナンス性が上がることが期待されています。
似たような概念としてコンポーネントがあり定義も似ていますが、ここではモジュールを使用します。
モジュールの分割
モジュール分割方法ですが、この記事では例として古くからあるレイヤードアーキテクチャ
の考えを基点に分割していきます。
以下のように、UI, Domain, Infrastructureに分けてみました。
さらに、Infrastructureを分割してみます。
レイヤードアーキテクチャの提唱者のMartin Flowerも指摘していますが、開発初期に大きく分けることは大変有効な反面、大規模になってくるとさらに詳細にモジュールを分けたほうが良い場合がほとんどになります。この記事では、Infrastructure層を分けて将来的に出てくるであろうメンテナンスなどの問題に対処することを考えています。
実装方法
(iOS開発の場合を説明しますが、基本的にmacOSやwatchOSでも使用できます。)
モジュールの分割方法として、Swift Package Manager(SPM)
を使用します。
Swift Packages
SPMは多くはライブラリとして使用されますが、ローカルライブラリ、つまりモジュールとしても使用できます。下記の公式記事ではリポジトリを分けていますが、今回の方法ではモジュールも同じリポジトリとしています。
Editing a Package Dependency as a Local Package
- プロジェクトを開く
- File > New > Packageを選択し、モジュール名を入力します。保存場所は、プロジェクト直下にします。同じgitリポジトリとして管理します。
- FinderからXcodeプロジェクトに、モジュール名のフォルダごと追加します。そうすることでモジュールはプロジェクトに依存関係として追加されます。
- 必要に応じて
Package.swift
を修正します。例えば、モジュールに他のモジュールをdependencyに追加する場合などです。
SPMを使用することによるメリット
モジュールとしてSPMを使用することによって、幾つものメリットがあります。
- 見通しの良さ:モジュールを分けることで、どのモジュールが何の機能を持っているのかの推測が簡単になります
- コンフリクトの解消:それなりの人数で開発を行っていると、必ずと言って良いほどgitのコンフリクトが発生しますが、最小限に抑えられます
- Package.swiftによる依存管理:どのモジュールがどの外部ライブラリに依存するかが明確になります
- 開発中のビルド時間の短縮:モジュールごとのビルドとなるため、あるモジュールを変更したとしても他のモジュールの再ビルドは行われないためビルド時間の短縮になります
参考:
ソースコード
実際に作成したプロジェクトはこちらになります。
ポイントは
-
UI
(つまりアプリ本体)はDomain
モジュールに依存しています -
Domain
モジュールはApi
,Infra
(DataStore)モジュールに依存しています -
Api
モジュールは外部ライブラリとしてAlamofire
に依存しています -
UI
からはApi
,Infra
にアクセスすることはできません - Domain/Infrastructure層については、
Factory
パターンでそれぞれのモジュールを疎結合にしています。
テストについて
モジュール化することで、テストが少ししづらく感じるかもしれませんが、XcodeのTest Plan
機能によって、モジュールも同時にテストできます。
- Product > Scheme > Edit Schemeを選択してからTestを選択状態にします。
- 「Convert to use Test Plan...」を押して
Test Plan
を作成します。 - プロジェクトナビゲーションから作成されたTest Planを選択し、「+」ボタンを押します。
- 追加したモジュールをTest Planに追加していきます。
モジュールに対してTest Planを使うことにより、モジュールごとにテストが可能になることが最大のメリットと言えます。テストのコードも管理もモジュールごとになるので、メンテナンスもしやすくなると言えます。
トラブルシューティング
モジュールとして追加したローカルSPMパッケージ関連でビルドエラーが出る
多くの場合、Files > Reset Package CachesとXcode再起動で解消できます。解消できない場合はPackage.swift
の設定が間違っている場合がほとんどです。
モジュールを追加した最初の方で起きることがあります。
Package.swiftのdependenciesにモジュールを追加したのに認識されない
依存を追加する場合、2つの場所に依存を書く必要があります。
- dependencies
- targetsの中のdependencies
まとめ
Swift Package Managerを使い、マルチモジュールを実現することができました。大規模なアプリにとっては大変有用な方法ですので、どんどん使っていきたいです。また、マルチモジュール化については様々な手法があるので、今後調べてまとめていきたいと思います。
Discussion