ある大規模なプロジェクトで、ビルド速度が3分以上と遅くなりました。通常、このような場合、スタジオは巨大なモノリスで作業しないようにプロジェクトのモジュール化を練習します。私たちSurfは、Appleの依存関係マネージャーであるSwift PackageManagerを使用してプロジェクトを実験してモジュール化することにしました。
結果については別の記事で説明しますが、ここで主な質問に答えます。なぜこれがすべて必要なのか、なぜSPMを選択したのか、そしてどのように最初のステップを踏んだのか。

なぜSPMなのか
答えは簡単です-それはネイティブで新しいものです。たとえば、Cocoapodsのようなxcworkspaceオーバーヘッドは作成されません。また、SPMは活発に開発されているオープンソースプロジェクトです。Appleとコミュニティは、バグの修正、脆弱性の修正、Swiftに続くように更新しています。
これにより、プロジェクトの構築が速くなりますか?
理論的には、アプリケーションをモジュール(フレームワーク)に分割するという事実により、アセンブリは高速化されます。つまり、各モジュールは、変更が加えられた場合にのみビルドされます。しかし、実験の終わりにのみ確実に主張することは可能です。
注:モジュール化の効果は、プロジェクトをモジュールに正しく分割することに直接依存します。
効率的なパーティショニングの作り方
分割方法は、選択したアーキテクチャ、アプリケーションのタイプ、そのサイズ、および今後の開発計画によって異なります。したがって、分割するときに遵守しようとする3つのルールについて説明します。
一般的な機能を共有します。各モジュールは、次のようなカテゴリを担当します。
- CommonAssets-アセットのセットとそれらにアクセスするためのパブリックインターフェイス。通常、SwiftGenを使用して生成されます。
- CommonExtensions-Foundation、UIKit、追加の依存関係などの拡張機能のセット。
個別のアプリケーションフロー。MainFlowがアプリケーションのメインフローであるツリー構造について考えてみます。ニュースアプリケーションがあるとしましょう。
- NewFlow-ニュースの画面と特定のニュースの概要。
- FavoritesFlow — .
- SettingsFlow — , , . .
reusable :
- CommonUIComponents — , UI-. .
- UI . , , , . .
特定のフローがあり、それに新しい関数を追加したいとします。コンポーネントが再利用される場合、または将来的に理論的に可能である場合は、別のモジュールまたはCommonUIComponentsのアナログに移動することをお勧めします。それ以外の場合は、コンポーネントをローカルのままにしておくことができます。
このアプローチは、コンポーネントの欠落の問題を解決します。これは大規模なプロジェクトで発生し、コンポーネントが文書化されていない場合、そのサポートとデバッグはその後不採算になります。
SPMを使用してプロジェクトを作成する
簡単なテストプロジェクトを作成することを検討してください。SwiftUIでマルチプラットフォームアプリプロジェクトを使用しています。プラットフォームとインターフェースはここでは関係ありません。
注:マルチプラットフォームアプリをすばやく構築するには、XCode12.2ベータ版が必要です。
プロジェクトを

作成して、以下を確認しましょう。次に、最初のCommonモジュールを作成しましょう。
- ディレクトリを作成せずにFrameworksフォルダを追加します。
- SPMパッケージCommonを作成します。

- サポートされているプラットフォームをPackage.swiftファイルに追加します。私たちはそれを持っている
platforms: [.iOS(.v14), .macOS(.v10_15)]

- 次に、モジュールを各ターゲットに追加します。iOS用のSPMExampleProjectとmacOS用のSPMExampleProjectがあります。

注:ルートモジュールのみをターゲットに接続するだけで十分です。それらはサブモジュールとして追加されません。
接続が完了しました。これで、パブリックインターフェイスを使用してモジュールを構成するだけで、最初のモジュールの準備が整いました。
ローカルSPMパッケージの依存関係を追加する方法
AdditionalInfoパッケージをCommonとして追加しましょう。ただし、ターゲットには追加しません。次に、CommonパッケージのPackage.swiftを変更しましょう。

他に何も追加する必要はありません。に使える。
現実に近い例
SwiftGenをテストプロジェクトに接続し、パレットモジュールを追加しましょう。デザイナーが承認したカラーパレットへのアクセスを担当します。
- 上記の手順に従って、新しいルートモジュールを作成します。
- スクリプトとテンプレートのルートディレクトリを追加します。
- Palette.xcassetsファイルをモジュールルートに追加し、カラーセットを書き留めます。
- 空のPalette.swiftファイルをSources / Paletteに追加します。
- パレット.stencilテンプレートをTemplatesフォルダーに追加します。
- 次に、SwiftGenの構成ファイルを登録する必要があります。これを行うには、swiftgen.ymlファイルをScriptsフォルダーに追加し、次のように記述します。
xcassets:
inputs:
- ${SRCROOT}/Palette/Sources/Palette/Palette.xcassets
outputs:
- templatePath: ${SRCROOT}/Palette/Templates/palette.stencil
params:
bundle: .module
publicAccess: true
output: ${SRCROOT}/Palette/Sources/Palette/Palette.swift

パレット
モジュールの最終的な外観パレットモジュールを構成しました。次に、ビルドの開始時にパレットが生成されるように、SwiftGenの起動を構成する必要があります。これを行うには、各ターゲットの構成に移動し、新しいビルドフェーズを作成します。これをパレットジェネレーターと呼びましょう。このビルドフェーズを可能な限り高い位置に移動することを忘れないでください。
ここで、SwiftGenの呼び出しを記述します。
cd ${SRCROOT}/Palette
/usr/bin/xcrun --sdk macosx swift run -c release swiftgen config run --config ./Scripts/swiftgen.yml
注:
/usr/bin/xcrun --sdk macosx
は非常に重要なプレフィックスです。これがないと、ビルドで「ターゲット 'x86_64-apple-macosx10.15の標準ライブラリをロードできません」というエラーが生成されます。

SwiftGen
Doneのサンプル呼び出し-色には次のようにアクセスできます:
Palette.myGreen
(SwiftUIの色タイプ)およびPaletteCore.myGreen
(UIColor / NSColor)。
水中の岩
私たちが遭遇したことをリストアップします。
- .
- SwiftLint & SwiftGen SPM. yml.
- Cocoapods. — , SPM . SPM Cocoapods - : «MergeSwiftModule failed with a nonzero exit code». , .
- SPM .
-L$(BUILD_DIR)
.
SPM — Bundler?
この件に関して、私はコメントで夢を見て議論することを提案します。このトピックはよく研究する必要がありますが、非常に興味深いものに見えます。ちなみに、SPMの長所と短所に関する興味深いかなり近い記事がすでにあります。
SPMを使用すると、Package.swiftをプロジェクトルートに追加することで、swiftrunを呼び出すことができます。それは私たちに何を与えますか?たとえば、fastlaneまたはswiftlintを呼び出すことができます。呼び出し例:
swift run swiftlint --autocorrect.