この記事はand factory.inc Advent Calendar 2022 6日目の記事です。
昨日は @nabetaro_jp さんの [Flutter] あの赤いエラー画面をカスタマイズする でした。
概要
先日、自分が参画しているプロダクトでライブラリ管理ツールをCarthageからSwift Package Manager(以下、SPM)に移行するという機会に遭遇したので、その過程で行ったことをまとめます。
背景
該当のiOSアプリのプロジェクトではライブラリ管理にCarthageとCocoapodsを併用しており、Carthageはビルド時間の短縮に大きく寄与していたものの、ライブラリによってはビルドや依存解消に失敗するケースが頻発していた(特にXcode12以降)こと、CI/CDの連携でエラーが起こる、XCFrameworksのサポートが怪しいなどの課題がこれまで存在していました。
そこでApple公式のパッケージマネージャーであるSPMがiOS開発者の間で広まってきたこともあり、今回Carthageで管理していたライブラリを思い切ってSPMとCocoapodsに移行する運びになりました。
やったこと
project.yml、Podfileの更新
弊アプリでは.xcodeprojファイルの生成にXcodegenを使用しているため、まずはproject.yml
にSPMに移行するライブラリを追記していきます。
...
packages:
# コミット指定の場合
FSPagerView:
url: https://github.com/WenchaoD/FSPagerView.git
revision: a8c14cfb2c07b91ccf6a19dfe126b86103f56f74
# バージョン指定の場合
Reusable:
url: https://github.com/AliSoftware/Reusable.git
from: 4.1.2
# ブランチ指定の場合
Lottie:
url: https://github.com/airbnb/lottie-ios.git
branch: master
...
Xcodegenを使わずXcode上で設定したい場合は、XcodeのFile
> Add packages
などから簡単に追加できます。
ライブラリの移行先をSPMにするかCocoapodsにするかは、当該ライブラリのリポジトリにパッケージ構成を定義するPackage.swift
があるかどうか、(SPM対応しているように見えても導入できないケースがあるので)実際に導入して正しく依存解消&ビルドできるか否かで判断しました。
Cocoapodsに移行する場合はPodfile
を更新します(説明は割愛)
Carthage関連のリソース削除
これでSPMからライブラリをインポートすることができるようになりました🎉
それに続き、不要になったCarthage関連のファイルを削除します。
整理対象はCartfile、Cartfile.resolvedと、後はproject.ymlのCarthage用のコードです(環境に合わせて要対応)
CIのビルドキャッシュ管理の設定
ここまでやれば特に問題なくビルドできるようになったのですが、我々のプロジェクトで使用しているCI上でビルドする際にキャッシュ管理をするためには別の設定が必要でした。
例としてCircleCIでSPMのキャッシュ管理するためには.circleci/config.yml
を下記のように更新します。
...
run_fastlane:
description: "Deploy fastlane"
steps:
- restore_cache: # SPM関連のキャッシュ対応のため
keys:
- v1-spm-cache-{{ checksum "ourproject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" }}
- v1-spm-cache-
- run:
name: Run fastlane
command: bundle exec fastlane "$CIRCLE_BRANCH"
no_output_timeout: 1h
- save_cache:
key: v1-spm-cache-{{ checksum "ourproject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" }}
paths:
- SourcePackages
...
やってることとしては、キャッシュを保存するパスを指定(ここではSourcePackages
)し、キーからこれを保存&呼び出しできるようにするという設定になります。
同様にCircleCI上でのビルドに使用するfastlaneのFastfile
にも、SPMのキャッシュファイルのパスを指定するオプションであるcloned_source_packages_path
を追記します。
desc "build develop"
lane :develop do
fix_version(build_configuration: "Debug")
gym(
scheme: SCHEME,
configuration: "Debug",
skip_package_ipa: true,
skip_archive: true,
skip_codesigning: true,
cloned_source_packages_path: "SourcePackages" # SPMキャッシュ用ディレクトリ
)
end
ビルド時間の壁を越えたい
これでCI上でも一度ビルドしたパッケージはキャッシュすることでビルド時間をある程度短縮できるようになりました。
しかし、それでもCarthage時代より30%ほどビルド時間が長い、って点で引っ掛かかりがありました。
原因を探ってみると、どうやらデータベース管理のためのライブラリであるRealm
がビルド時間の足を引っ張っていたことが判明。
そこでRealm
のみSPMではなくXCFrameworkでプロジェクトに導入する流れになりました。
XCFrameworkでRealmを入れる
XCFrameworkとは、ビルド済みのSwiftパッケージを配布するためのフォーマットのこと。
これまでCarthageを使っていた時はあらかじめライブラリをframework化してキャッシュすることでビルド時間を短縮していたのですが、残念なことにSPMではライブラリのソースコードはキャッシュしてくれるが、これをビルドしたバイナリはCIでキャッシュできない(キャッシュしても機能しない)という事実が調べていて分かりました。
では実際の導入ですが、Realmは公式リポジトリで最新版のxcframeworkを配布してくれているので、やるこことしてはこれを落としてプロジェクトにリンクするだけです。簡単ですね。
上記ページから欲しいバージョンのCarthage.xcframework.zip
をダウンロードします。
ダウンロードが完了したらこれを解凍し、xcframeworks管理用の任意のディレクトリに移し、これをプロジェクトにリンクするための設定をproject.yml
に追記します。
...
dependencies:
- framework: xcframeworks/Realm/Realm.xcframework # 追加
- framework: xcframeworks/Realm/RealmSwift.xcframework # 追加
- package: APIKit
- package: CryptoSwift
...
実際のプロジェクトではこの辺りの処理を自動化するためのスクリプトを下記記事参考に書いています。
ここまでやって、漸くSPM+Cocoapods+XCframeworksの編成でビルド時間をほぼ以前の水準まで戻すことに成功しました🎉
補足(XCFrameworks導入後に実機ビルドで失敗した件)
XCFrameworkの導入作業の途中、シミュレーターやCIではビルドできるのに実機ではビルドに失敗するという事象に見舞われました。
色々原因を調べた結果、project.yml上でRealmに依存するTarget(下記でTargetB
)だけにdependenciesとframeworkの設定をしていたことが原因らしく、このTargetに依存する別のTarget(下記でTargetA
)にも同じ設定をしてあげる必要がありました。
targets:
TargetA:
...
dependencies:
- target: TargetB
...
- framework: xcframeworks/Realm/Realm.xcframework # TargetAが直接依存していなくても設定しないと実機ビルドで失敗
- framework: xcframeworks/Realm/RealmSwift.xcframework # (同様)
...
TargetB:
...
dependencies:
- framework: xcframeworks/Realm/Realm.xcframework
- framework: xcframeworks/Realm/RealmSwift.xcframework
...
これはXCFrameworkがそういう仕様なのか、Xcodegenがそういう仕様なのかよく分かっていないので、今後の要調査課題として残っております。何かご存知の方がいらっしゃれば教えてください。
まとめ
CarthageからSPMへの移行が一通り終わってみての感想ですが、重い腰を上げるのが中々大変でしたが、いざやってみるとすぐに終わったという感じです。
ただビルド時間の側面ではまだまだ改善ポイントがありそうなので、引き続き色々模索していこうと思います。
明日のAdvent Calendarもお楽しみにしてください!