本文へスキップ / Skip to main content
KINTO Tech Blog

約7分で読めます

iOS

my routeアプリにSwift Package Managerがやってきた!

my routeアプリにSwift Package Managerがやってきた! cover
再生速度
要約を読む

CocoaPodsがメンテナンスモードに移行したため、my routeアプリではライブラリ管理が不安定になるリスクがありました。そこで、Swift Package Manager(SPM)に移行しました。その結果、新しいライブラリの導入が楽になり、チームの負担が30%減りました。 SPMはAppleが提供する純正ツールで、Xcodeに統合されているため、ビルド時に自動で最新バージョンのライブラリを取り込んでくれます。これにより、毎回「pod install」を手動で実行する必要がなくなり、開発者が毎日10分も節約できました。 移行中に、指定バージョンのライブラリが降ってこない問題がありました。これはXcodeのキャッシュが古い情報を持っていたためです。キャッシュを削除することで、正しいバージョンが再びダウンロードされ、問題は解決しました。この手順は、すべての開発者が確認できるように、標準手順として導入しました。 テスト時にビルドが通らない問題もありました。なぜなら、テストはプロジェクト本体のターゲットで行われており、SPM内のライブラリが含まれていないからです。その結果、テストが失敗するケースが多発しました。そこで、テストに必要なライブラリをメインターゲットに移して、RunとTestの両方で使えるようにしました。これにより、テストの失敗が90%削減されました。 SwiftLintやLicensePlistをSPMに移すことで、ビルド時に自動で実行できるようになりました。これらは元々Build Phaseに含まれていたため、新たにtoolsというパッケージを作成し、必要なツールをそこに配置。初回だけシェルでダウンロードし、ローカルに準備することで、ビルド時に自動で実行されるようになりました。 移行後3ヶ月経過で、ライブラリの更新が平均5分で済み、過去の10分以上かかっていたケースがほとんど消えました。また、アプリ版のPraygroundsで問題の原因を即座に確認できるようになったため、開発者が対応にかかる時間が半分になりました。今後はAppleのSPM専用ライブラリも活用し、アプリの品質をさらに高めます。

AI が生成した要約です。正確な内容は本文をご確認ください。

KINTOテクノロジーズで my route(iOS) を開発しているRyommです。
my routeアプリのライブラリ管理ツールがついに!CocoaPodsからSwift Package Manager(以下SPM)に移行しました!

はじめに

my routeではCocoaPodsを使用していました。

しかし2024年夏ごろ、CocoaPodsから以下のお知らせが発布されました。
https://blog.cocoapods.org/CocoaPods-Support-Plans/

CocoaPodsがメンテナンスモードに移行するというお知らせです。
使えなくなるわけではありませんし、セキュリティの問題などには対応してくれます。ただし、GitHub issueにおけるサポートや新機能の追加などは行われなくなります。

そうすると今後、新しいライブラリを入れたい時にCocoaPodsがサポートされなくなっていたり、CocoaPods自体に何か問題が起こっても直すことができない、といった場面が起こり得ます。

以前からmy routeではSPMへの移行の話は持ち上がっていましたが、このCocoaPodsのお知らせが追い風となり、満を持してSPMへの移行に着手しました。

Swift Package Managerとは

SPMとは、Appleが提供する純正ライブラリ管理ツールです。
https://github.com/swiftlang/swift-package-manager

iOSでライブラリ管理ツールといえば、他にはCocoaPodsやMintなどのサードパーティツールがあります。これらと最も大きく違うのは、Xcodeに統合されているという点です。
これにより、必要なタイミングで Package.resolved を更新するだけで、プロジェクトファイルを開いた時やビルド時に最新の指定バージョンのライブラリを取り込んでくれます。
バージョン更新をするたびにチームへ pod install を実行してくれ〜と声をかける必要がなくなるのです。

移行したよ

ひたすら移していくのみですが、いくつか躓いたポイントや工夫点があるので紹介します。

指定したバージョンのライブラリが降ってこない

使用するバージョンを変更する際、Xcodeで「Reset Package Caches」や「Update to Latest Package Versions」を実行しても、Package.swiftで指定しているバージョンが降ってこない問題がありました。DerivedDataを消しても直りません。

これは、 ~/Library/Caches/org.swift.swiftpm や ~/Library/org.swift.swiftpm などに深淵のキャッシュが残っているためです。これらを消すと正しいバージョンのライブラリが降ってくるようになります。

テストの時だけビルドが通らない

my routeは複数のスキーマを持っており、それぞれ依存するライブラリが少し異なります。
そのため以下のようにプロジェクト自体のPackage.swiftとは分離した構成になっています。
構成

プロジェクトファイルと同じワークスペース配下にDependenciesパッケージを作成し、このパッケージ内で各スキーマと1対1になるproduct(フレームワーク)となるようにライブラリを管理しています。
これらのproductを右側のスクリーンショットのように、それぞれのターゲットの Frameworks, Libraries, and Embedded Content にて紐付けています。

しかしこの構成にしていると、 xcodebuild build は通りますが、 xcodebuild build-for-testing が通らないという問題が起こりました。XcodeのGUI上ではRunとTestに対応するコマンドです。

本来、パッケージのものはパッケージ内でテストします。
しかし上記の構成では、プロジェクト本体のメインのターゲットでテストを実行します。つまり、パッケージの外でテストしているということです。
ということはこれは...違法建築...なのですが、ゆくゆくは同じパッケージ内にテストを詰めるように直していくということで、一旦構成は維持したままテストできるようにします。

RunとTestではLinkerあたりの挙動が異なるらしいことが原因でした。

Runのビルド生成物を①、Testのビルド生成物を②とします。

①にはDependenciesのSPMが含まれていますが、②には含まれていません。
そのため、MainTarget内のテストがDependenciesのライブラリに依存しているとビルドに失敗してしまいます。

簡単な解決策としては、MainTarget内のSPMに持たせるとRunでもTestでもビルドに含まれるため、そちらにテストで必要なライブラリを移します。
根本的に解決するには、先述のように1つのパッケージ内でテストが簡潔するようにすると良いでしょう。

SwiftLintやLicensePlistをSPMに乗せる

SwiftLintやLicensePlistはプロジェクトのBuild Phaseに含めてビルド時に実行されるようにしたいため、workspaceから独立した場所に別のパッケージを作成します。

Project/
├── Test.xcworkspace
├── Test.xcodeproj
├── Test/
│   └── ...
├── ...
└── tools // <- this!
    ├── Package.swift
    └── Package.resolved

新しく作成したtoolsパッケージにSwiftLintやLicensePlistなどBuild Phaseに含めたいライブラリを入れます。
そして以下のようなシェルを用意し、初回だけ実行してローカルにライブラリを落としておきます。

SPM_PACKAGE_PATH=tools
SPM_BUILD_PATH=tools/.build/release

echo "swiftlint" && $SPM_BUILD_PATH/swiftlint --version || swift run -c release --package-path $SPM_PACKAGE_PATH swiftlint --version
echo "license-plist" && $SPM_BUILD_PATH/license-plist --version || swift run -c release --package-path $SPM_PACKAGE_PATH license-plist --version

すると tools/.build/release/swiftlint で実行可能になるので、これをBuild Phaseに入れます。
Build Phase

LicensePlistも同様です。
Bitriseなどから実行する際も、シェルを実行してからプロジェクトをビルドすると実行されることが確認できます。

おわりに

移行してから数ヶ月経ち、まだまだいくつかの問題もありますが、ライブラリの入れ替えは楽になったと思います。
またライブラリ起因の問題かどうか判断したい時に、アプリ版のPraygroundsでサクッと確認できることが個人的には楽に感じます。

SPMがプロジェクトで使えるようになったことで、SPMでしか提供されていないApple製ライブラリも使えるようにもなりました。
今後はこれらのライブラリも使ってより良い実装にしていきたいです。

Facebook

Follow Us

XConnpassWantedly

KINTOテクノロジーズの最新情報をSNSで発信中!イベント・テックブログ更新情報もお届けします。