Private RepositoryのRelease Assetsにアップロードした、XCFrameworkをより簡単に利用する
※この記事は Luup Developers Advent Calendar の21日目の記事です。
こんにちは。はじめまして。iOSエンジニアの山手です。本業では、公共交通系のiOSアプリの開発に携わりつつ、Luupでは、業務委託としてiOSアプリの機能改修や品質改善等のお手伝いをさせていただいています。
今回の記事は、Private RepositoryのRelease Assetsに対してXCFrameworkのzipファイルをアップロードし、PrivateなSwift PackageのBinary targetとして利用するテクニックの紹介になります。
Swift PMでBinary frameworkを配布する
Swift Packageでは、XCFramework形式のBinary frameworkを配布する機能が、Xcode11からサポートされています。
これにより、ソースコードを隠匿化した状態で、Frameworkを配布できるようになりました。
Objective-CとSwiftが混在した状態のFrameworkやC++のコードを含むFrameworkもXCFrameworkを介すことで、Swift Package化できるようになっています。
LUUP iOSアプリでは、Swift Package対応が行われていないサードパーティーのFrameworkをSwift Packageで管理するために、Swift PMのBinary Targetを活用しています。
let package = Package(
name: "SampleFramework",
products: [
.library(
name: "SampleFramework",
targets: ["SampleFramework", "SampleFrameworkLocal"]
)
],
targets: [
.binaryTarget(
name: "SampleFrameworkRemoteBinary",
url: "https://hogehoge/fugafuga/SampleFrameworkRemote.xcframework.zip",
checksum: "9b588da829930de437275973d5e627c8b85ec068ae524758c6f12b2b4412a3ea"
),
.binaryTarget(
name: "SampleFrameworkLocalBinary",
path: "path/to/SampleFrameworkLocal.xcframework"
)
]
)
上記は、パッケージのターゲットにBinary Targetを宣言した例です。
Binary Targetの宣言には2つの方法があります。
- XCFrameworkをRepositoryに含めず、リモートのストレージなどに置き、URLを指定する
- Repositoryのバンドルとして含め、XCFrameworkをパス指定する
XCFrameworkをSwift Packageで配布することは、どちらの方法でも実現できますが、デメリットも存在します。
1つ目のリモートのストレージなどに置く場合は、Swift Package Managerを介した認証周りへの対応、アップロードしたzipファイルのChecksumの取得、Package.swift
の更新が必要になります。
2つ目のRepositoryのバンドルとして含める場合は、Checksumの記載が不要になりPackage.swift
の更新は不要になります。しかし、RepositoryにBinaryを含めるため内包するFrameworkによっては、Repositoryサイズの肥大化に繋がります。
手間を少なく実現するのであれば、2番のローカル指定です。
しかし、Repository自体の容量が増えるため、将来的にgitの動作速度にも影響する可能性があります。
また、RealmなどBinary化後のファイルサイズが100MBを超えるようなFrameworkの場合、GitHubへのPushが出来ません。
できる限り、プロジェクトのRepositoryには外部FrameworkのBinaryを含めず、Binaryはリモートに置いたものを取得するべきと考えています。
GitHubのRelease Assetsを活用する
リモートにFrameworkのBinaryを置くとなった時、どこにアップロードするかという問題が生まれます。
GitHubを用いて開発する場合は、Release Assetsに対してBinaryをアップロードすることを考える方が多いのではと思います。
実際、Release Assetsは、Git LFSと異なりストレージ容量や帯域の消費をしないため、コスト的にもメリットが大きいです。
Public Repositoryであれば、認証なくRelease AssetsにアップロードしたBinaryをBinary TargetのURLとして指定可能ですが、社外からも利用・参照できるため、PrivateなFrameworkを含める場合やPublic Repositoryへの機密情報を誤Pushするリスクなどを考えるとPrivate Repositoryで運用することが望ましいです。
しかし、Private RepositoryのRelease AssetsをBinary TargetのURLとして利用するには、GitHubの認証周りの対応コストやRelease Assets周りの更新手順が手動運用では、複雑になるという問題も抱えています。
Private RepositoryでXCFrameworkを活用する際の課題、解決策については、以前に下記の記事にまとめています。
Private RepositoryのRelease Assetsを利用したSwift Packageを実現するFastlane Pluginを作った
Private RepositoryのRelease Assetsを利用したSwift Packageを実現するために必要な作業を自動化するFastlaneのPluginを開発、公開しました。
XCFrameworkの生成コストなどを考え、現在はローカル環境下での利用を想定し、実装しています。
(Private Repositoryでの運用を考え、開発していますが、Public RepositoryのRelease Assetsを活用する場合でも、利用できます)
Plugin開発に至った背景
LUUP iOSアプリでは、Swift Package Manager未対応のFrameworkをXCFramework化して、Swift Package Managerで管理しています。
PublicなSwift Package未対応のFrameworkは、Public Repositoryで管理、PrivateなXCFramework形式で提供されているFrameworkは、アプリ本体のRepositoryに含める形で利用しています。
現状でも問題なく運用できていますが、ヒューマンエラーによる機密情報の漏洩リスクやアプリ本体のRepository容量の肥大化のリスクを抱えています。
リスクを最小限にするため、Private Repository上で、Swift Package未対応のFrameworkをSwift Package化すべきと考えました。
Private Repositoryで運用するためのひと工夫必要なため、手動で更新作業を行う場合、保守運用コストが高くなってしまいます。
そこで、必要な作業を自動化するFastlaneのPluginを開発に至りました。
Private RepositoryのRelease Assetsを利用したSwift Packageを利用する手順
Public Repositoryでは、認証不要なので簡単にRelease AssetsにアップロードしたXCFrameworkを利用することができますが、Private Repositoryでは、ひと工夫必要です。
必要な手順は、下記の通りです。
- Swift Package未対応のFrameworkのビルド、XCFrameworkの生成
- 生成した、XCFrameworkをzipファイルに圧縮する
- zipファイルのChecksumを取得する
- XCFrameworkをアップロードするためのReleaseを発行する
- 発行したReleaseのAssetsに対して、zip圧縮したXCFrameworkをアップロードする
- GitHub APIまたは、GitHub CLIを介して、AssetのURLを取得する
- AssetsのURL、zipファイルのChecksumを用いて、
Package.swift
のBinary Targetを更新する -
Package.swift
更新後のReleaseを発行する
GitHubの各リリースに表示されるAssetsへのリンクは、zipファイルそのもののホスティング先のURLではないため、GitHub APIまたは、GitHub CLIを介して、Asset URLを取得する必要があります。
そのため、Private Repositoryで運用する場合、Release AssetsにアップロードしたAsset URLをPackage.swift
を更新する前に取得する必要があります。
今回開発したPluginでは、Step2からStep7までの手順を自動化しています。
XCFrameworkの生成は、導入するプロジェクトによって事情が異なると考えたため、Pluginでは行っていません。
Pluginを利用するための事前準備
GitHub CLIのセットアップ
ReleaseやPull Requestの発行、Release Assetsの取得、更新にGitHub CLIを利用します。
事前に、GitHub CLIの事前セットアップが必要です。
必要なファイルの配置
プロジェクトのディレクトリー直下に./XCFrameworks
ディレクトリーを作成し、生成したXCFrameworkを配置します。
また、下記のPrivatePackageConfig.yml
をディレクトリー直下に配置します。
# デフォルトブランチ名
default_branch_name: "main"
# パッケージ名
package_name: "PrivateXCFrameworkPackagingExampleFramework" # パッケージ名
# Package.swiftのProductの配列に含まれるLibraryの項目値
libraries:
# Library名
- name: "SampleFramework"
# Libraryが内包するターゲット名の配列
targets:
- "SampleFramework"
# Package.swiftのTargetの配列に含まれるBinary targetの項目値
binary_targets:
- "SampleFramework"
Pluginが実行する処理内容
-
PrivatePackageConfig.yml
記載のデフォルトブランチからBinary配布用のReleaseを発行する -
./XCFrameworks
ディレクトリー配下に配置されたXCFrameworkをzip圧縮する - XCFrameworkのzipファイルのChecksumを取得する
- XCFrameworkのzipファイルをStep1で、発行したReleaseのAssetsとしてアップロードする
- GitHub CLIを介して、Step4でアップロードしたzipファイルのAsset URLを取得する
-
Package.swift
更新作業のWorkブランチをデフォルブランチからチェックアウトする - Step3、Step5で取得したURL、Checksumを
Package.swift
に反映する -
Package.swift
更新後、Pull Requestを自動発行する
利用手順のStep2〜7の項目を自動的に行い、Pull Requestの発行までを実行します。
発行されたPull Requestをマージ後、手動でReleaseを発行することで、任意のXCFrameworkを含むSwift PackageをPrivate Repositoryで利用可能です。
LUUP iOSアプリへの導入
現時点では、Advent Calendar向けにpluginの開発までとなっており、LUUP iOSアプリには導入できていない状況です。
GitHub API経由でRelease Assetsにアクセスするため、HTTP接続時の認証にGitHub Personal Access Tokenを用いる関係で、
Organizationのトークンポリシーの確認が必要であったり、Xcode Cloudでの動作確認が残っています。
まだまだ、導入課題は残っていますが、実現に向けて着実と進めていきたいと思います。
まとめ
今回、Public Repositoryで運用せざるを得ないRepositoryのPrivate化を狙い、簡単なFastlane Pluginを作成してみました。
今回開発したPluginによって、Xcode 13.3以後で利用できるようになったPrivate RepositoryのRelease Assetsを用いたSwift Packageをより実現しやすくなったと思います。
まだ、導入課題は残っていますが、同様の課題を抱えているiOS開発者の方の参考になれば幸いです。
Discussion