マルチモジュール勉強メモ~EmbeddedFramework~
Embedded Frameworkとは
- プロジェクトのマルチモジュール化
ざっくりいうとフレームワークのこと
通常新しくProjectを作成するとTargetは一つだけど、Project内の要素をFrameworkとして抜き出して使うことができる
例えばUI,Core,Data,CommonというレイヤーでProjectが構成されていて、これをEmbedded Frameworkを使ってレイヤーごとにtargetを切り分けたりする
| 通常 | マルチモジュール化 | マルチモジュール化 |
|---|---|---|
使用する場合の注意点
Embedded Frameworkを使ってアプリ内の要素をFrameworkとして切り出した場合
通常Frameworkを使うとき同様に使用するファイルでimportする必要がある
なのでFrameworkとして切り出された側で、他ターゲットから利用することを想定しているメソッドなどの場合はアクセスコントロールとして先頭にpublicをつけなければならない
これはアクセスコントロールがデフォルトではinternalであるため
メリット
-
コード共有
共通分をFrameworkとして切り出すことで、他のプロジェクトで利用したり、UIだけ別に作って機能が同じであるアプリをいくつも横展開したりできる
他にも一つのサービスをiOSアプリだけでなくwatchOSやtvOSなど複数端末で使えるようにしたりなどマルチプラットフォームへ展開することもできる -
レイヤー分割・依存関係の矯正
先に書いたとおり、切り出されたターゲットはFrameworkとして扱われるため
通常Frameworkを使うとき同様にimportしないとコンパイルエラーとなり使うことができない
これがレイヤー感の依存関係の矯正にどう影響するかというと
各レイヤーの依存関係は予め設定することができるから
例えば以下のように設定してみました
| メインターゲット | Core | Data | Common |
|---|---|---|---|
この場合メインターゲットはimport Coreやimport Data, import Commonを使用したいファイルで宣言することで各レイヤーにアクセスことができます
同様にCoreでDataを使いたい場合はimport Dataやimport Commonを必要に応じて宣言、Dataではimport Commonを必要に応じて宣言することでそれぞれ利用することができます
逆にこの依存関係だとCommonからDataだったりDataからCoreだったり、Coreからメインターゲットへアクセスすることはimportを使って宣言してもコンパイルエラーで弾かれてしまいます
なのでEmbedded Frameworkを使うとレイヤー分割や依存関係の矯正ができるといえます
以下の記事がとても参考になります
- ビルド時間の短縮
Swift1.2で差分ビルドが導入されビルド速度は改善されたはず
ただ自分は実感してないけど、過去には差分ビルドがうまく走らずに少しのコード修正にも関わらず全体のビルドが走ったりすることもあったらしい
*ただこれに関しては自分の実感がない&参照していた記事が古いのでもしかしたら2021年では問題ないのかもしれない
推測でしかないけど、1つのターゲットにすべてがまとまっている&コード量が多い場合に差分ビルドが走らないことがあったのかもしれない
というのもEmbedded Frameworkを使うことで差分ビルドが効いたという内容は見かけるから
Embedded Frameworkで依存関係を明確にすることでコンパイラにそれが伝わり
一つ一つのレイヤーごとに差分を確認させることができる...???
またメインターゲット実行時に差分ビルドが効きやすく速くなるだけでなく、XcodeのScheme設定で特定のFrameworkを選択することで選択したFrameworkのみのビルドやテスト実行などを行えるのでビルドが速くなる
SchemeをそれぞれのFrameworkに設定してビルドしてみるとわかります
依存関係にあるものまでしかビルドは行わないのでScheme設定が下位レイヤーになればなるほど依存関係がすくなくなりビルドが速くなります
テストをしたい場合はこのScheme設定を対象のFrameworkにするとよさそうです
| メインターゲット | Core | Data | Common |
|---|---|---|---|
差分ビルドがうまく機能せずに少しの修正で全くさわってない箇所まで再ビルドがかかるのであれば
確かに無駄
考えてみると、全てのソースファイルをアプリケーションターゲットでビルドするのは無駄です。APIからデータを取得するコードやデータモデルなどは、最初に定義してしまえばほとんど修正することはありません。
https://devblog.thebase.in/entry/2018/05/09/113141
PreProcessor
Compilerへプログラムを送り込めるように変換
マクロをそれぞれの定義へ置換
依存関係の発見
PreProcessorのディレクティブ(#if DEBUGなど)を解決
差分ビルドがうまく効かないっていうのは
このPreProcessorが正確に依存関係を認識できないから
全部つながっているように見えて起こるのかも?
Embedded Frameworkを使うとこの依存関係をPreProcessorが明確に認識できるから差分ビルドが効くようになるのかも
【メモ】
> Dynamic Frameworkが増えるにつれてアプリの起動時間が長くなることがあります。
Dynamiic Frameworkを使うとアプリの起動時間が長くなる?
ModuleをDynamic Frameworkではなく Static Framework としてビルドして使用することで起動時間の最適化が可能?
StatickFramework化して起動時間短縮?
extension使うとビルド時間って長時間化するの?
> Module毎に言語のMigrationが出来る
知らなかった
いまあるFlutterとかKMMの着想はここなのかな
[Cons] Main Interfaceなどの仕組みが扱いづらい
しかし、現状はStoryboard上ではBundle ResourcesをModule間で共有できる仕組みがないので
自分の場合はアプリ全体で使うasset管理がこれで手こずった
結局Assets.xcassetsをメインターゲットとCommonモジュールにもおいて対応
両方に置いたのは基本的にはassetsをCommonに置くんだけど
AppIconがメインターゲットで使うからどうしてもメインターゲットでも必要だったため
いずれにしてもstoryboardとかMain Bundle Resourceに該当するものはEmbedded Frameworkを使う場合は気をつけないといけないな
上記の設計には『下のレイヤーは上のレイヤーのことを知らない』という原則があります。しかし、SwiftではPackage単位ではなくModule単位で名前空間が管理されているので、同一のModuleに複数のレイヤーが含まれていると下のレイヤーの人が上のレイヤーの人を呼び出すことを機械的に防ぐことは出来ません
下位レイヤーから上位レイヤーアクセスできるじゃん....そういうもんなんだなってプログラミング始めたばかりのときにおもってたけど他言語だとできないことが多いのか。しらなかった
EmbedFrameworkを使いすぎると起動が遅くなる件について
これはEmbedded Frameworkに限ったことではなくて、ほかにもCarthageを利用してダイナミックフレームワークを追加していくと起動時間が長くなっていくようです
Appleは多くとも6つのダイナミックフレームワークにすることを推奨しているとか
static frameworkはembedしちゃだめ 関連メモ