【Xcode/iOS】Swift Package Managerでライブラリを自作&公開する方法
この記事からわかること
- Swift Package Managerとは?
- XcodeでSPMのパッケージを自作する方法
- リソースを含めるには?
- Package.swiftとは?
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.3
- iOS:18.0
- Swift:5.9
- macOS:Sequoia 15.4
Swift Package Manager(SPM)とは?
「Swift Package Manager(SPM)」はアプリ開発時に使用できるパッケージ(ライブラリ)管理ツールです。他にも「Carthage」や「Cocoa Pods」などがありますがSPMはApple純正の管理ツールでXcodeにも統合されているので主流になりつつあるツールになります。
今回はSPMで使用できるライブラリを自作・公開・導入する方法をまとめていきます。
SPMライブラリの作成方法
XcodeからSPM用のライブラリを作成するには上部メニュー「File」>「New」>「Package...」をクリックし「Library」を選択します。
続いてテストシステムを含めたい場合は選択して「Next」をクリックします。
PC内の保存先を問われるので任意の場所を選択します。この際に「Don't add to any project or workspace」ではなく「Target」を選択するとそのプロジェクトに生成したパッケージを導入した状態にしてくれます。
これでライブラリ用のプロジェクトが完成しました。
中を確認すると以下のような構造になっています。基本的にSourcesディレクトリの中に各ターゲットに対応するソースコードやリソースを配置する構造になっています。
├── MyLibrary
│ ├── Package.swift
│ ├── Source
│ └── MyLibrary // ターゲット
│ └── MyLibrary.swift // 中身のソースファイル群
最初に生成される「Package.swift」ファイルはマニフェストファイルとも呼ばれ、プロジェクトの依存関係、ターゲット、およびその他のプロジェクト設定が記述されているファイルです。これに関しては後述しておきます。
あとはライブラリとして切り出したい機能をファイルを追加しながら定義していくだけです。Swiftではデフォルトのアクセス修飾子が別モジュールからはアクセスできないinternalのため、 別のモジュールからアクセスできるようにしたい場合はpublicを付与してあげてください。
// デフォルトではinternalのため別モジュールからはアクセスできない
class TestLibrary {
var text = "Hello, World!"
}
// 別のモジュールからアクセスできるようにしたい場合はpublicを付与する
public class TestPublicLibrary {
// デフォルトではinternalのため別モジュールからはアクセスできない
var text = "Hello, World!"
// イニシャライザにもpublicを付与しないと別のモジュールからインスタンス化できない
public init(text: String = "Hello, World!") {
self.text = text
}
// publicを指定しているので別モジュールからアクセス可能
public func sayHello() {
print(text)
}
}
プラットフォームの違いに注意
ライブラリではどのプラットフォームで使えるようにするかを意識する必要があります。後述している「Package.swift」ファイルで以下のように指定することはできますが開発する際にはツールバーで指定しているビルド先を正しく指定するようにしてください。
platforms: [
.iOS(.v16)
],
例えば以下のようにmacOSを指定していた場合はUIKitが使用することができないのでNo such module 'UIKit'というエラーが発生してしまいます。iOS向けであればここをiOSのものに変更してあげればエラーが解消されます。
マルチプラットフォームで開発する場合はcanImport(XXXX)も使用することができるので活用してみてください。
#if canImport(UIKit)
import UIKit
#endif
コマンドで作成する
ちなみにSPM用のライブラリはコマンドで作成することも可能です。このコマンドはカレントディレクトリ内にパッケージに必要なファイルを生成するため、空のディレクトリを作成してから実行してください。
// オリジナルライブラリパッケージ作成コマンド
$ swift package init --name パッケージ名 --type library
リソース(画像やカラー)を含ませる
公式リファレンス:Bundling resources with a Swift package
パーケージの中にリソースを含ませるにはまずそのまま.xcassetsファイルを用意するだけです。ここに画像やカラーを定義すれば自動でリソースファイルを認識して使用できるようになります。配置する場所はターゲットディレクトリの配下に設置するようにしてください。
├── MyLibrary
│ ├── Package.swift
│ ├── Source
│ └── MyLibrary // ターゲット
│ ├── Resource // ターゲットの配下に設置する
│ ├── Colors.xcassets
│ └── Images.xcassets
│ └── MyLibrary.swift
定義したリソースを参照する時にはBundle.moduleを指定する必要があります。これを指定しないと正しく取得できないので注意してください。
let image: Image = Image("sample", bundle: .module)
let uiImage: UIImage = UIImage(named: "sample", in: .module, compatibleWith: nil)!
テキストファイル(txt・json...)を含ませる
.xcassetsファイルで定義しないテキストファイル(txt・json)などのリソースは明示的にリソースファイルであることを「Package.swift」に記述します。
├── MyLibrary
│ ├── Package.swift
│ ├── Source
│ └── MyLibrary
│ ├── Resource
│ └── sample.txt
│ └── MyLibrary.swift
例えば上記のように「sample.txt」を配置した場合はtargetの中にresourcesで配列を定義し、process("ディレクトリ名")で定義してあげる必要があります。これをしないとリソースとして認識されないので注意してください。
targets: [
.target(
name: "MyLibrary",
resources: [
.process("Resource")
]
),
]
あとはBundle.moduleで取得することができます。
let bundle = Bundle.module
guard let url = bundle.url(forResource: "sample", withExtension: "txt") else {
return nil
}
try? String(contentsOf: url, encoding: .utf8)
パッケージを公開する
パッケージ(ライブラリ)を作成できたらGitHubにアップロードします。アップロードすることで誰でも使えるパッケージとして公開することが可能です。特に変わったところはなく必要なのはタグ付けを行うことです。このタグがパッケージのバージョン管理になります。
$ git tag 1.0.0
GitHubへのアップロードはターミナルからのGitだけでなくXcodeからも操作できるので参考にしてください。
Package.swift
「Package.swift」には一番最初は以下のように記述されています。英語の部分は雑に翻訳してます。中身はSwiftで記述されているので多少読みやすいかもしれません。基本構造としてpackageという変数が定義されそこにPackageがインスタンス化されているようなコードになっています。
import PackageDescription
let package = Package(
name: "MyLibrary",
products: [
// 製品は、パッケージが生成する実行可能ファイルとライブラリを定義し、それらを他のパッケージから見えるようにします。
.library(
name: "MyLibrary",
targets: ["MyLibrary"]),
],
targets: [
// ターゲットはパッケージの基本的な構成要素であり、モジュールまたはテストスイートを定義します。
// ターゲットは、このパッケージ内の他のターゲットや依存関係にある製品に依存できます。
.target(
name: "MyLibrary"),
]
)
中に指定できる内容をまとめていきます。
name・・・パッケージの名前
name: "MyLibrary",
platforms・・・対象のプラットフォーム
platforms: [
.iOS(.v14) // 対象OSとバージョンを定義
],
products・・・このパッケージが外部に提供するライブラリや実行可能ファイル
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]),
],
dependencies・・・外部パッケージの依存を定義(Gitリポジトリとバージョン)
dependencies: [
// メジャーバージョンが変わらない範囲で最新まで許可
// 5.6.0 以上、かつ 6.0.0 未満まで許可
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.6.0")
// from: "5.6.0" => .upToNextMajor(from: "5.6.0")の省略形なので下と同義
.package(
url: "https://github.com/Alamofire/Alamofire.git",
.upToNextMajor(from: "5.6.0")
)
// 他にも
// .upToNextMinor(from: "1.2.3")
// .range("1.1.0" ..< "1.5.0")
// .exact("1.2.3")
// .branch("main")
// .revision("abcdef123456...")
// etc...
],
targets・・・実際のコード(モジュール)の定義。ライブラリの中核。
targets: [
.target(
name: "MyLibrary",
dependencies: [
"Alamofire"
]
),
// テストコード用のターゲット
.testTarget(
name: "YourTargetTests",
dependencies: ["YourTarget"]
)
]
パッケージを導入する
SPMはXcodeから使用できるようので公開したパッケージを導入してみます。上部のメニューから「File」>「Add Packages...」をクリックします。
右上部の入力欄にリポジトリのURLを直接入力することで指定可能なので先ほど公開したリポジトリURLを入力してみてください。
これでSPMパッケージを自作・公開・導入することができました。一応サンプルでhttps://github.com/amefure/Sample-SPM-Libraryに公開してあるので導入してみてください。
おすすめ記事:GitHub:Sample-SPM-Library
エラー:Build input file cannot be found
パッケージ開発中にエラーに以下のようにエラーになることがあります。これはファイルやディレクトリのリネームを行った際に発生することがあります。Xcodeではパッケージを都度キャッシュしてビルドを効率化する仕組みがあり、リネームしてもキャッシュが残っているとこのようなエラーを吐いたします。
Build input file cannot be found: '/Users/XXXXXX/Desktop/Sample-SPM-Library/Sources/CoreModule/CoreModule.swift'. Did you forget to declare this file as an output of a script phase or custom build rule which produces it?
これを解消する方法は簡単でXcode上部メニュー>「File」>「Packages」>「Reset Package Caches」をクリックするだけです。
公開したパッケージをクローンして改修する
GitHubに公開していたパッケージをクローンして改修しようと思った際に普通にクローンしたところどうやってXcodeで開くのか不明でした。色々触ってみるとどうやらXcodeを立ち上げて最初の画面で「Clone Git Repository...」から対象のパッケージをクローンする必要があるみたいです。
この方法でクローンすると.swiftspmディレクトリが生成されその中のpackage.xcworkspaceからパッケージをXcodeで起動させることができるみたいです。
├── MyLibrary
│ ├── .git
│ ├── .gitignore
│ ├── .swiftspm
│ ├── configuration
│ └── xcode
│ └── package.xcworkspace
│ ├── Source
│ └── MyLibrary
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。





