はじめに
今回Swift Package Manager
を導入してみました。
Swift Package Manager
自体はすでに数年前からあり、Xcode11が発表された段階でXcodeに統合されました。
そのため既に導入しているプロジェクトが多いと思います。
しかし現在継続して運用しているプロジェクトではXcodeGen
+CocoaPods
という環境であり導入が遅れていました。
その上記のような環境である点を踏まえ、導入手順やつまづいた箇所をまとめました。
なぜSwift Package Manager
導入に思い至ったか
ビルド時間の解消に向けて
CocoaPods
を導入しており、なおかつCocoaPodsBinary
も導入していました。
その為ある程度はプロジェクトビルド時におけるPods自体のビルド時間は抑えられていましたが、Binary化出来ずそのままになっていたPodが何個かありました。
プロジェクトの構成の問題もあるかもしれませんが、これらのPodが差分ビルド時でも毎回ビルドされていました。
Swift Package Manager
に導入することにより毎回ビルドがされなくなるのではと考え導入を行いました。
導入して変わったこと
- [初回]
xcodegen generate
後にResolve Packagesが発生 → +30~40秒ほど - [毎回] 1回当たりのビルド時間改善 → -5~10秒ほど
上記の結果はPodsを複数Swift Package Manager
に移行した合算になります。
移行したものは下記の通りになります。
- SVGKit
- Reachability
- GoogleSignIn
- Realm
- AFNetworking
- Firebase
- RxSwift
- RxOptional
- RxDataSources
- SwiftyXMLParser
移行したPodsが増えるほど1回当たりのビルド時間は減りますが、Resolve Packagesの時間は増えていきます。
しかしResolve Packagesはxcodegen generate
後の初回のみ待てばよいのでトータルではマイナスになっています。
現在の環境
- Xcode 14.0.1
- XcodeGen 2.29.0
- CocoaPods 1.11.3
- CocoaPodsBinary 0.4.4
導入について
導入の手順
手順は以下の通りとなります。
- Podfileから対象のPodの記載を削除
- project.ymlの
packages:
にPackageを記載 - project.ymlの
targets:
にPackageを記載
1.Podfileから対象のPodの記載を削除
まず、Podfileから対象のPodを削除します。
Podでバージョンを指定していた場合、Swift Package Managerでも指定するのでメモして下さい。
次にpod install
コマンドによりPodfile.lockを更新します。
これによりCocoaPodsで使用していたライブラリが削除されます。
2.project.ymlのpackages:
にPackageを記載
Projectで導入するPackageを記載していきます。
まずはXcodeでPackageが提供されているか確認して下さい。
確認手順は以下の通りとなります。
- Xcodeを開く
- File -> Add Package から
Swift Package Manager
のウィンドウを開く(画像参照) - ウィンドウ右上の検索窓にURLを記入
- 対象のPackageが表示され、プロジェクトに追加できればOK
次に確認したPackageをproject.ymlに記載します。
packages:
の配下にPackage名とURL、そしてブランチやバージョン等の指定が必要になります。
name: SampleProject
packages:
Firebase:
url: https://github.com/firebase/firebase-ios-sdk
exactVersion: 8.15.0
GoogleSignIn:
url: https://github.com/google/GoogleSignIn-iOS
exactVersion: 6.1.0
Realm:
url: https://github.com/realm/realm-swift
exactVersion: 10.16.0
こちらは備考になります。
Packageのバージョン指定には他の指定方法も行えます。
よく使うであろう記述方法をピックアップしておきます。
exactVersion: 8.15.0 // バージョン指定:8.15.0を指定
maxVersion: 8.15.0 // バージョン指定:8.15.0以下を指定
branch: master // ブランチ指定:masterを指定
他の細かな指定方法についてはこちらを確認して下さい。
3.project.ymlのtargets:
にPackageを記載
Targetsで導入するPackageを記載します。
こちらはPackageによってはproduct:
の指定まで必要になります。
targets:
SampleTarget:
dependencies:
- package: GoogleSignIn
- package: Realm
product: RealmSwift
- package: Firebase
product: FirebaseAnalytics
- package: Firebase
product: FirebaseMessaging
- package: Firebase
product: FirebaseCrashlytics
- package: Firebase
product: FirebasePerformance
Packageにどのようなproduct
が含まれているかはPackage.swift
を閲覧することで確認出来ます。
XcodeのShow the Project Navigator
からPackageを選択することでPackage.swift
を確認することが出来ます。
let package = Package(
name: "Realm",
platforms: [
.macOS(.v10_10),
.iOS(.v11),
.tvOS(.v9),
.watchOS(.v2)
],
products: [
.library(
name: "Realm",
targets: ["Realm"]),
.library(
name: "RealmSwift",
targets: ["Realm", "RealmSwift"]),
],
導入について - 個別編
FirebaseCrashlytics
CocoaPods
で導入している場合、BuildPhasesにスクリプトを記述していると思います。
しかしCocoaPods
からSwift Package Manager
に変更することにより書き換える必要があります。
- TARGESのBuildPhasesを開く
- RunScriptで行っているスクリプトを書き換える
- 旧:
${PODS_ROOT}/FirebaseCrashlytics/run
- 新:
${BUILD_DIR%Build/*}SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run
- 旧:
XcodeGenの場合はBuildPhasesでの書き換えではなく、project.yml内のscripts:
を書き換える必要があります。
まとめ
今後について
1回当りのビルド時間は減らすことが出来ました。
しかしブランチを切り替えてxcodegen generate
を走らせた際のResolve Packagesで待たされるのが億劫に感じます。
キャッシュ化が行えないのか調べてみましたが基本的にはライブラリ側がxcframeworkに対応していないと難しそうです。
xcodegen自体から脱却するもありかと思いますので今後はそちらを調べてみようかと思います。