To migrate a Capacitor plugin to Swift Package Manager, add a Package.swift Pindahkan file iOS code ke direktori root plugin yang nama paket dan produknya sesuai dengan apa yang diharapkan oleh Capacitor CLI. ios/Sources/<Target>, replace the Objective-C CAP_PLUGIN file-file penghubung CAPBridgedPlugin menambahkan kinerja kompatibilitas dalam Swift, dan menambahkan Package.swift kepadatan ke npm files Daftar. Tahan podspec agar aplikasi CocoaPods tetap berfungsi.
Mengingat Capacitor 8, cap add ios membuat proyek SPM secara default. Sebuah plugin tanpa Package.swift terlewatkan dalam aplikasi-aplikan tersebut dengan peringatan “Beberapa Capacitor yang diinstal tidak kompatibel dengan SPM”, dan pengguna kemudian melihat “plugin tidak diimplementasikan” pada saat runtime. Panduan ini membahas migrasi manual, alat konverter, dan bagian-bagian yang paling panduan lain lewatkan: aturan penamaan, sumber daya, code Objective-C dan dependensi pihak ketiga.
Tim aplikasi yang pindah ke proyek mereka sendiri sebaiknya membaca Bagaimana Migrasi Aplikasi Capacitor Anda ke SPM sebaliknya.
Bagaimana Capacitor mengonsumsi paket Anda
Pada bunx cap sync ios, CLI mengubah ios/App/CapApp-SPM/Package.swift dalam aplikasi. Untuk setiap Capacitor plugin yang memiliki Package.swift, ia menambahkan dua baris:
.package(name: "CapgoCapacitorUpdater", path: "../../../node_modules/@capgo/capacitor-updater")
// ...
.product(name: "CapgoCapacitorUpdater", package: "CapgoCapacitorUpdater")
Dua konsekuensi:
- Package Anda dikonsumsi oleh jalur lokal dari
node_modules, bukan dari URL Git. Package AndaPackage.swiftharus berada di root package npm, dan semua yang merujuk ke harus diterbitkan ke npm. - Nama package dihasilkan dari nama npm, bukan dari podspec Anda. CLI menghilangkan
@, mengubah/dan-context_Migrasi Plugin Capacitor ke Swift Package Manager
| Paket npm | Nama Paket dan Produk yang Diperlukan |
|---|---|
@capacitor/haptics |
CapacitorHaptics |
@capgo/capacitor-updater |
CapgoCapacitorUpdater |
capacitor-my-plugin |
CapacitorMyPlugin |
@acme/capacitor-scanner |
AcmeCapacitorScanner |
If your Package.swift Menggunakan nama atau nama produk lain, aplikasi gagal menyelesaikan paket dengan kesalahan seperti product 'X' required by package 'capapp-spm' target 'CapApp-SPM' not found.
Langkah 1: tulis Package.swift
Berikut adalah tata letak yang digunakan oleh plugin resmi. Berikut adalah manifest yang sebenarnya dari @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")
]
)
Catatan:
- Tetapkan
swift-tools-version: 5.9kecuali Anda memerlukan fitur yang lebih baru. Paket yang dihasilkan aplikasi juga menggunakan 5.9 secara default, dan Capacitor 8 belum secara resmi mendukung Swift 6. - Pilih
from: "8.0.0"untukcapacitor-swift-pm. Aplikasi memasang versi yang tepat yang sesuai dengan versi yang terinstal@capacitor/iosdan rentang memungkinkan SPM menyelesaikan baik itu.branch:atauexact:Karena menggunakannya akan menyebabkan konflik penyelesaian. - Nama target dapat apa saja, tetapi menjadi nama modul Swift. Hindari nama umum seperti
Pluginyang bertabrakan dengan plugin lain.
Langkah 2: Pindahkan sumber
SPM mengharapkan satu folder per sasaran. Konverter dan template resmi menggunakan:
my-plugin/
├── Package.swift
├── MyPlugin.podspec
├── ios/
│ ├── Sources/
│ │ └── MyPlugin/
│ │ ├── MyPlugin.swift
│ │ └── MyPluginImplementation.swift
│ └── Tests/
│ └── MyPluginTests/
│ └── MyPluginTests.swift
└── package.json
Kemudian hapus apa yang tidak perlu SPM. ios/Plugin.xcodeproj, ios/Plugin.xcworkspace, ios/Podfile, ios/Plugin/Info.plist and ios/PluginTests/Info.plist.
Perbarui podspec agar CocoaPods mengompilasi file yang sama:
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'
Update podspec sehingga CocoaPods mengompilasi file yang sama:
Langkah 3: Ganti bridge Objective-C dengan CAPBridgedPlugin Plugin.m:
#import <Capacitor/Capacitor.h>
CAP_PLUGIN(MyPlugin, "MyPlugin",
CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(startScan, CAPPluginReturnCallback);
)
SwiftPM tidak dapat mencampur Objective-C dan Swift dalam satu target, jadi ini dipindahkan ke kelas 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
// ...
}
}
Aturan Petaan:
identifieris the first argument ofCAP_PLUGIN(nama kelas).jsNameadalah argumen kedua, nama yang digunakan diregisterPlugin('MyPlugin')dalam TypeScript Anda.- Each
CAP_PLUGIN_METHODmenjadi satuCAPPluginMethod, dengan tipe return yang sama (CAPPluginReturnPromise,CAPPluginReturnCallbackatauCAPPluginReturnNone).
A method yang hilang dari pluginMethods Kompilasi berhasil dan gagal hanya ketika JavaScript memanggilnya. Cari dengan @objc func dengan sebuah CAPPluginCall bersarang dan gagal hanya ketika JavaScript memanggilnya. Cari dengan Plugin.h and Plugin.m.
CAPBridgedPlugin berfungsi dengan CocoaPods juga, jadi file Swift yang sama dapat digunakan oleh kedua pengelola paket.
Langkah 4: Perbarui package.json dan .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"
}
}
The -scheme Nama paket Anda adalah __CAPGO_KEEP_0__. Jangan lupa Package.swift or ios/Sources atau files Alasan utama plugin bekerja dari cekout Git dan bermasalah setelah npm publishatau bun pm pack --dry-run nilai ini adalah nama paket Anda. Lupa
Tambahkan hasil kompilasi SPM ke .gitignore:
.build/
Package.resolved
/Packages
.swiftpm/
nilai ini adalah nama paket Anda. Lupa
Ketergantungan Pihak Ketiga
A garis podspec seperti s.dependency 'Alamofire', '~> 5.9' menjadi ketergantungan paket dan produk pada target Anda:
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")
]
Jika SDK hanya mengirimkan .xcframeworkshipkannya di dalam paket npm dan gunakan target biner:
.binaryTarget(name: "VendorSDK", path: "ios/Frameworks/VendorSDK.xcframework")
Jika vendor tidak menawarkan paket SPM dan tidak ada xcframework, Anda tidak bisa menambahkan dukungan SPM. Tahan plugin CocoaPods saja dan tuliskan di README.
Tahan rentang versi di Package.swift dan podspec sejajar. Dua plugin di aplikasi yang sama yang memerlukan versi SDK yang tidak kompatibel akan gagal resolusi di manapun manager paket.
Sumber daya dan manifest privasi
Gambar, JSON, storyboard dan PrivacyInfo.xcprivacy harus dideklarasikan:
.target(
name: "MyPlugin",
dependencies: [/* ... */],
path: "ios/Sources/MyPlugin",
resources: [
.process("Resources"),
.copy("PrivacyInfo.xcprivacy")
])
CocoaPods memerlukan file yang sama dideklarasikan di podspec. Masukkan mereka ke dalam bundle sumber daya bernama:
s.resource_bundles = {
'MyPluginResources' => [
'ios/Sources/MyPlugin/Resources/**/*',
'ios/Sources/MyPlugin/PrivacyInfo.xcprivacy'
]
}
Dalam code, sumber daya SPM hidup di Bundle.modulesedangkan CocoaPods memasukkannya di dalamnya MyPluginResources.bundleberikutnya di samping kelas plugin. SWIFT_PACKAGE flag, yang SwiftPM mendefinisikan secara otomatis:
#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
Objek-C nyata code
Jika bagian dari plugin tersebut adalah Objective-C (bukan hanya bridge), masukkan ke dalam target sendiri dengan header publik di dalam include folder, dan membuat target Swift bergantung pada folder tersebut:
.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")
The Swift code then does import MyPluginObjC.
Rute Otomatis: cap2spm
__CAPGO_KEEP_0__-plugin-converter capacitor-plugin-converter membangun cap2spm binary yang membaca Plugin.m dan Plugin.h, menambahkan CAPBridgedPlugin konformitas ke kelas Swift Anda, menghasilkan Package.swift, memindahkan file ke Sources dan Tests, memperbarui podspec dan package.json, dan menghapus file proyek Xcode lama.
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
Itu dirancang untuk plugin yang hanya Swift-nya kecuali file bridge. Jalankan di atas cabang Git yang bersih dan kemudian lakukan Langkah 5 secara manual.
Opsi lainnya:
- Membuat plugin segar dengan
bun create @capacitor/plugindan salin implementasi Anda di sana. Template sudah mendukung SPM dan CocoaPods. Ini seringkali lebih cepat untuk plugin kecil. - Menggunakan agen. The
capacitor-plugin-spm-supportkeahlian dalam Capgo Keahlian menggambarkan Package.swift, pembersihan bridge, sumber daya danpackage.json. Pasang denganbunx skills add Cap-go/capgo-skills.
Tes di aplikasi SPM nyata
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
Periksa bahwa ios/App/CapApp-SPM/Package.swift menampilkan daftar paket Anda, dan sinkron telah mencetak “Semua Capacitor plugin memiliki file Package.swift”. Kemudian bangun di perangkat dan panggil setiap metode. Ulangi di aplikasi yang dibuat dengan bunx cap add ios --packagemanager CocoaPods untuk memastikan podspec masih berfungsi.
Troubleshooting
product 'X' required by package 'capapp-spm' ... not found. Nama Paket atau Produk tidak sesuai dengan nama yang dihasilkan dari nama npm Anda. Lihat tabel di atas.
"MyPlugin" plugin is not implemented on ios. Entah Package.swift belum dipublikasikan, jsName tidak sesuai, registerPluginatau kelas kurang, CAPBridgedPlugin. Periksa folder plugin di node_modules.
Multiple targets named 'Plugin' atau kesalahan modul duplikat. Dua plugin menggunakan nama target yang sama. Ubah milik Anda menjadi sesuatu yang unik. Tim aplikasi juga dapat mengatasi masalah ini dengan experimental.ios.spm.packageOptions (moduleAliases atau symlink) di konfigurasi Capacitor yang tersedia sejak CLI 8.4.
target 'X' contains mixed language source files. Berkas Objective-C dan Swift berbagi folder. Cabut mereka menjadi dua target.
Xcode shows stale package errors after fixes. atau File > Paket > Reset Paket CacheKemudian bangun lagi.
Jika Anda juga memerlukan fitur opsional yang dapat ditonjolkan per aplikasi, baca bagaimana menggunakan fitur paket SPM di CapacitorUntuk aplikasi yang harus tetap menggunakan CocoaPods untuk saat ini, lihat bagaimana menggunakan CocoaPods dengan Capacitor 8, dan untuk gambaran yang lebih besar, SPM vs CocoaPods untuk Capacitor.