Capacitor プラグインを Swift Package Manager へ移行するには、プラグインのルートに Package.swift を追加し、そのパッケージ名とプロダクト名が Capacitor の CLI が期待するものと一致するようにします。次に、iOS の code を ios/Sources/<Target>に移動し、Objective-C の CAP_PLUGIN ブリッジファイルを Swift の CAPBridgedPlugin 準拠に置き換え、 Package.swift を npm の files リストに追加します。CocoaPods アプリが引き続き動作するように、podspec は保持してください。
Capacitor 8 以降、 cap add ios はデフォルトで SPM プロジェクトを作成します。 Package.swift がないプラグインは、これらのアプリでスキップされ、「Some installed Capacitor plugins are not compatible with SPM」という警告が表示されます。その後、ユーザーは実行時に「plugin is not implemented」を確認します。本ガイドでは、手動での移行、コンバーターツール、そして多くのガイドが取り上げない命名規則、リソース、Objective-C の code およびサードパーティ依存関係について説明します。
自社のプロジェクトを移行するアプリチームは、 Capacitor アプリを SPM に移行する方法 こちらを読んでください。
Capacitor がパッケージを利用する方法
オンラインに bunx cap sync ios、CLI の書き換え ios/App/CapApp-SPM/Package.swift アプリ内で行います。各 Capacitor プラグインについて、 Package.swiftがある場合、2 行追加します:
.package(name: "CapgoCapacitorUpdater", path: "../../../node_modules/@capgo/capacitor-updater")
// ...
.product(name: "CapgoCapacitorUpdater", package: "CapgoCapacitorUpdater")
2つの注意点:
- パッケージは、
node_modulesからの git URL ではなく、ローカルパスから消費されます。あなたのPackage.swiftは npm パッケージのルートに配置する必要があり、参照するすべてのものは npm に公開されている必要があります。 - 名前は npm の名前から派生されます、podspec ではなく。CLI が削除します
@、変換します/と-次のように分類しています_、各セグメントをキャメルケースに変換し、先頭文字を大文字にします:
| npm パッケージ | 必須のパッケージ名とプロダクト名 |
|---|---|
@capacitor/haptics |
CapacitorHaptics |
@capgo/capacitor-updater |
CapgoCapacitorUpdater |
capacitor-my-plugin |
CapacitorMyPlugin |
@acme/capacitor-scanner |
AcmeCapacitorScanner |
が Package.swift 別の名前やプロダクト名を使用している場合、次のようなエラーでパッケージの解決に失敗します product 'X' required by package 'capapp-spm' target 'CapApp-SPM' not found.
ステップ 1: Package.swift の作成
これは公式プラグインが使用するレイアウトです。実際のマニフェストは次のとおりです: @capacitor/haptics 8:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "CapacitorHaptics",
platforms: [.iOS(.v15)],
products: [
.library(
name: "CapacitorHaptics",
targets: ["HapticsPlugin"])
],
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
],
targets: [
.target(
name: "HapticsPlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm")
],
path: "ios/Sources/HapticsPlugin"),
.testTarget(
name: "HapticsPluginTests",
dependencies: ["HapticsPlugin"],
path: "ios/Tests/HapticsPluginTests")
]
)
注意:
- 保持
swift-tools-version: 5.9新しい機能が必要でない限り。アプリの生成パッケージもデフォルトで 5.9 を使用しており、Capacitor 8 はまだ Swift 6 を公式にサポートしていません。 - 使用する
from: "8.0.0"をcapacitor-swift-pm。アプリはインストール済みのものと一致する正確なバージョンを固定します@capacitor/ios、範囲を指定すると SPM が両方を解決できます。branch:またはexact:解決の競合を引き起こします。 - ターゲット名は任意ですが、Swift モジュール名になります。
Pluginなど、他のプラグインと競合する一般的な名前は避けてください。
ステップ 2: ソースファイルの移動
SPM はターゲットごとに 1 つのフォルダを期待します。コンバーターと公式テンプレートは以下を使用します:
my-plugin/
├── Package.swift
├── MyPlugin.podspec
├── ios/
│ ├── Sources/
│ │ └── MyPlugin/
│ │ ├── MyPlugin.swift
│ │ └── MyPluginImplementation.swift
│ └── Tests/
│ └── MyPluginTests/
│ └── MyPluginTests.swift
└── package.json
次に、SPM で不要なものを削除します: ios/Plugin.xcodeproj, ios/Plugin.xcworkspace, ios/Podfile, ios/Plugin/Info.plist と ios/PluginTests/Info.plist.
podspec を更新して、CocoaPods が同じファイルをコンパイルするようにします:
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
s.ios.deployment_target = '15.0'
s.dependency 'Capacitor'
s.swift_version = '5.1'
ステップ 3: Objective-C ブリッジを CAPBridgedPlugin に置き換え
従来のプラグインは、次の場所でメソッドを登録します: Plugin.m:
#import <Capacitor/Capacitor.h>
CAP_PLUGIN(MyPlugin, "MyPlugin",
CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(startScan, CAPPluginReturnCallback);
)
SwiftPMでは1つのターゲット内でObjective-CとSwiftを混在させられないため、これはSwiftクラスに移動します:
import Foundation
import Capacitor
@objc(MyPlugin)
public class MyPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "MyPlugin"
public let jsName = "MyPlugin"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "echo", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "startScan", returnType: CAPPluginReturnCallback)
]
@objc func echo(_ call: CAPPluginCall) {
call.resolve(["value": call.getString("value") ?? ""])
}
@objc func startScan(_ call: CAPPluginCall) {
call.keepAlive = true
// ...
}
}
マッピングルール:
identifierは〜の第1引数ですCAP_PLUGIN(クラス名)。jsNameは第2引数で、〜で使用される名前ですregisterPlugin('MyPlugin')(TypeScript内)。- 各
CAP_PLUGIN_METHODは1つの〜になりますCAPPluginMethod、戻り値の型はそのまま維持します(CAPPluginReturnPromise,CAPPluginReturnCallbackまたはCAPPluginReturnNone).
〜からメソッドが抜けていても pluginMethods コンパイルは通り、JavaScriptから呼び出した時だけ失敗します。grepして @objc func 次のものを使用して: CAPPluginCall パラメータを数え、配列と照合します。その後、削除します Plugin.h と Plugin.m.
CAPBridgedPlugin CocoaPodsでも動作するので、同じSwiftファイルを両方のパッケージマネージャーで使えます。
ステップ 4: package.json と .gitignore の更新
{
"files": [
"android/src/main/",
"android/build.gradle",
"dist/",
"ios/Sources",
"ios/Tests",
"Package.swift",
"MyPlugin.podspec"
],
"scripts": {
"verify:ios": "xcodebuild -scheme CapacitorMyPlugin -destination generic/platform=iOS"
}
}
この -scheme 値はパッケージ名です。これを忘れると Package.swift または ios/Sources における files git checkoutでは動くのに、その後壊れる最もよくある原因です。 npm publish. 実行 bun pm pack --dry-run して、ファイルリストを確認します。
に SPM ビルド出力を追加 .gitignore:
.build/
Package.resolved
/Packages
.swiftpm/
ステップ 5: コンバーターが処理できないものへの対処
サードパーティ依存関係
のような podspec の行は s.dependency 'Alamofire', '~> 5.9' ターゲットのパッケージ依存関係とプロダクトになります:
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0")
],
targets: [
.target(
name: "MyPlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "Alamofire", package: "Alamofire")
],
path: "ios/Sources/MyPlugin")
]
が SDK だけを配布する場合 .xcframework、npm パッケージ内に同梱し、バイナリターゲットを使用します:
.binaryTarget(name: "VendorSDK", path: "ios/Frameworks/VendorSDK.xcframework")
ベンダーが SPM パッケージも xcframework も提供していない場合、SPM サポートはまだ追加できません。プラグインは CocoaPods のみとして維持し、README にその旨を明記してください。
バージョン範囲を Package.swift と podspec で整合させます。同じアプリ内で互換性のないバージョンの SDK を要求する 2 つのプラグインがあると、どちらのパッケージマネージャーでも解決に失敗します。
リソースとプライバシーマニフェスト
画像、JSON、ストーリーボード、 PrivacyInfo.xcprivacy は宣言する必要があります:
.target(
name: "MyPlugin",
dependencies: [/* ... */],
path: "ios/Sources/MyPlugin",
resources: [
.process("Resources"),
.copy("PrivacyInfo.xcprivacy")
])
CocoaPods でも podspec に同じファイルを宣言する必要があります。名前付きリソースバンドルに配置してください:
s.resource_bundles = {
'MyPluginResources' => [
'ios/Sources/MyPlugin/Resources/**/*',
'ios/Sources/MyPlugin/PrivacyInfo.xcprivacy'
]
}
code では、SPM リソースは Bundle.moduleに配置され、CocoaPods ではその MyPluginResources.bundleの隣、プラグインクラスのそばに配置されます。 SWIFT_PACKAGE フラグで切り替えます。SwiftPM はこれを自動的に定義します:
#if SWIFT_PACKAGE
let resourceBundle = Bundle.module
#else
let resourceBundle: Bundle = {
let classBundle = Bundle(for: MyPlugin.self)
guard let url = classBundle.url(forResource: "MyPluginResources", withExtension: "bundle"),
let bundle = Bundle(url: url) else {
return classBundle
}
return bundle
}()
#endif
実際の Objective-C code
プラグインの一部が Objective-C (ブリッジだけでなく) で書かれている場合は、パブリックヘッダーを include フォルダに配置し、Swift ターゲットがそれに依存するようにします:
.target(
name: "MyPluginObjC",
path: "ios/Sources/MyPluginObjC",
publicHeadersPath: "include"),
.target(
name: "MyPlugin",
dependencies: [
"MyPluginObjC",
.product(name: "Capacitor", package: "capacitor-swift-pm")
],
path: "ios/Sources/MyPlugin")
Swift の code は以下を実行します import MyPluginObjC.
自動化ルート: cap2spm
Ionic チームの capacitor-plugin-converter は cap2spm を読み取る Plugin.m と Plugin.hバイナリを構築し、 CAPBridgedPlugin を Swift クラスに追加し、 Package.swiftを生成し、ファイルを Sources と Tests、podspec を更新し package.json、古い Xcode プロジェクトファイルを削除します。
curl -OL https://github.com/ionic-team/capacitor-plugin-converter/releases/latest/download/cap2spm.zip
unzip cap2spm.zip
xattr -d com.apple.quarantine ./cap2spm # binary is unsigned
./cap2spm --backup /path/to/my-plugin
ブリッジファイルを除いてSwiftのみで構成されるプラグイン向けです。クリーンなgitツリーで実行した後、ステップ5を手動で行ってください。
その他のオプション:
- 新しいプラグインをスキャフォールドする を使用して
bun create @capacitor/plugin実装をコピーしてください。テンプレートはすでにSPMとCocoaPodsをサポートしています。小規模なプラグインの場合は、こちらの方が速いことが多いです。 - エージェントを使う。 この
capacitor-plugin-spm-supportのスキルが Capgo のスキル Package.swift、ブリッジのクリーンアップ、リソースを順に説明し、package.json。インストールはbunx skills add Cap-go/capgo-skills.
実際のSPMアプリでテストする
bun create @capacitor/app spm-test
cd spm-test
bun add @capacitor/ios
bun add ../my-plugin
bun run build
bunx cap add ios # SPM is the default in Capacitor 8
bunx cap sync ios
以下を確認してください: ios/App/CapApp-SPM/Package.swift パッケージが一覧に含まれていること、および sync が “All Capacitor plugins have a Package.swift file” と出力したことを確認してください。その後、実機でビルドして各メソッドを呼び出します。作成したアプリでも繰り返し、 bunx cap add ios --packagemanager CocoaPods podspec が引き続き動作することを確認します。
トラブルシューティング
product 'X' required by package 'capapp-spm' ... not found. パッケージ名または製品名が、npm の名前から派生した名前と一致しません。上記の表を参照してください。
"MyPlugin" plugin is not implemented on ios. 以下のいずれか: Package.swift が公開されていない、 jsName が一致しない、 registerPlugin、またはクラスに不足がある CAPBridgedPlugin。プラグインフォルダ内を確認し、 node_modules.
Multiple targets named 'Plugin' 、またはモジュールの重複エラーを確認してください。 2つのプラグインが同じターゲット名を使用しています。自分のプラグインを一意の名前に変更してください。アプリ開発チームは次の方法で回避することもできます: experimental.ios.spm.packageOptions (moduleAliases または symlink) は Capacitor 設定で、CLI 8.4 以降で利用できます。
target 'X' contains mixed language source files. Objective-C と Swift のファイルが同じフォルダを共有しています。これらを2つのターゲットに分割してください。
修正後も Xcode で古いパッケージのエラーが表示される。 使用する File > Packages > Reset Package Cachesを実行してから、再度ビルドしてください。
アプリごとにオプション機能を切り替える必要がある場合も、次を参照してください。 Capacitor で SPM パッケージの traits を使う方法。当面 CocoaPods を使い続ける必要があるアプリは、こちらを参照してください。 Capacitor 8 で CocoaPods を使う方法、さらに全体像については、 Capacitor の SPM と CocoaPods 比較.