关于 capacitor-plugin-spm-support
capacitor-plugin-spm-support 是一项技能,指导在保留 CocoaPods 支持的同时,为 Capacitor 插件添加 Swift Package Manager 支持,涵盖源码转换、包配置和验证。
主要功能
- 01SPM 清单与目标配置
- 02转换 Swift 插件注册
- 03保留 CocoaPods podspec
- 04配置依赖与资源
- 05验证软件包和应用
适用场景
- 01让插件可安装于基于 SPM 的应用
- 02处理缺少 Package.swift 的警告
- 03准备面向 Capacitor 9 的插件分发
- 04检查 SPM 与 CocoaPods 构建
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-plugin-spm-support安装后在 Claude Code 中输入/capacitor-plugin-spm-support确认已加载。
附带文件metadata.jsonreferences/package-swift-templates.md
详细介绍
为 Capacitor 插件添加 Swift Package Manager 支持
让插件可安装到基于 SPM 的 Capacitor 应用中,同时为 CocoaPods 应用保留其 podspec。
适用场景
在以下情况下触发:
- 插件仓库包含
ios/Plugin/*.swift和*.m/*.h,其中有CAP_PLUGIN(...),且根目录没有Package.swift。 - 用户维护的插件在应用执行
npx cap sync ios时,输出<plugin id> does not have a Package.swift。 - 插件在 CocoaPods 中运行正常,但在 SPM 应用中,JS 收到
"X" plugin is not implemented on ios。 - 为 Capacitor 9 / CocoaPods Trunk 只读做好插件准备。
不要用于:
- 应用侧移除 CocoaPods →
cocoapods-to-spm。 - 插件的 Capacitor 主版本升级 →
capacitor-plugin-upgrades、capacitor-plugin-upgrade-v8-to-v9。 - 仅涉及 Android 的插件工作,或非 Capacitor 插件(纯 Cordova、React Native)。
CLI 强制执行的规则(容易忽略)
Package.swift必须位于 npm 包根目录(与package.json同级),而不能放在ios/下。应用 CLI 只会检查<plugin root>/Package.swift。- CLI 会在应用的
CapApp-SPM/Package.swift中以.package(name: "<Name>", path: ...)和.product(name: "<Name>", package: "<Name>")引用插件,其中<Name>根据 npm 名称生成:去掉@,将/和-转换为单词分隔符,再转为 PascalCase。@capgo/capacitor-updater->CapgoCapacitorUpdater,@acme/capacitor-foo-bar->AcmeCapacitorFooBar。Package(name:)和.library(name:)产品都必须等于<Name>,否则应用构建时无法解析该产品。 - 运行时注册使用
ios/App/App/capacitor.config.json中的packageClassList,该列表通过扫描插件的 iOS 源文件(package.json中的capacitor.ios.src,默认为ios)并查找@objc(ClassName)来填充。请在类上保留@objc(<Class>Plugin);没有它,插件就永远不会注册。 capacitor-swift-pm依赖必须使用from: "<major>.0.0"。如果主版本与应用中的 Capacitor 不同,应用同步会在node_modules中重写该版本,并警告<id> is built for Capacitor <N>, it might cause issues。- Capacitor 9:只依赖
.product(name: "Capacitor", package: "capacitor-swift-pm")。移除无条件添加的Cordova产品;该产品仅存在于安装了 Cordova 插件的应用中。只有当代码导入 Cordova 时,才在 Capacitor 8 中保留它。
操作步骤
1. 检查
阅读 package.json(名称、files、capacitor.ios.src、脚本)、.podspec(s.name、s.ios.deployment_target、s.dependency、s.resources、s.vendored_frameworks)、ios/ 中的每个文件,以及 .m 文件中的 CAP_PLUGIN(...) 宏。
记录:SPM <Name>(按上述规则)、类名、jsName、每个 CAP_PLUGIN_METHOD(name, returnType)、iOS 部署目标、第三方 pod、资源、供应商提供的框架,以及桥接文件以外的 ObjC/C 源文件。
检查每个第三方 pod 是否有官方 SPM 包。编辑前报告没有官方 SPM 包的依赖;询问是要移除、作为 .xcframework 引入,还是让插件仅支持 CocoaPods。
2. 快速路径:转换器
如果 iOS 代码除 [Name]Plugin.m / .h 桥接文件外仅包含 Swift,官方转换器即可完成大部分工作(添加 CAPBridgedPlugin、创建 Package.swift、将代码移至 ios/Sources 和 ios/Tests、移除 Plugin.xcodeproj/xcworkspace/Podfile,更新 podspec 中的 source_files、files 和 verify:ios):https://github.com/ionic-team/capacitor-plugin-converter。根据上述规则检查其差异,尤其要检查包名和产品名。
3. 手动转换
- 移动源文件:
ios/Plugin/->ios/Sources/<ClassName>/,测试文件 ->ios/Tests/<ClassName>Tests/。 - 转换该类(方法名和返回类型必须与
.m宏中的完全一致):import Capacitor @objc(FooPlugin) public class FooPlugin: CAPPlugin, CAPBridgedPlugin { public let identifier = "FooPlugin" // first CAP_PLUGIN argument public let jsName = "Foo" // second CAP_PLUGIN argument (registerPlugin name) public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "doThing", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "watch", returnType: CAPPluginReturnCallback), CAPPluginMethod(name: "fireAndForget", returnType: CAPPluginReturnNone) ] @objc func doThing(_ call: CAPPluginCall) { call.resolve() } } - 删除
FooPlugin.m和FooPlugin.h(CAP_PLUGIN桥接文件),以及ios/下过时的Plugin.xcodeproj、Plugin.xcworkspace、Podfile、Info.plist文件。 - 添加根目录下的
Package.swift。模板和变体(资源、二进制目标、第三方包、混合 ObjC)见references/package-swift-templates.md。 - 更新 podspec:
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}',保留s.dependency 'Capacitor',并同步部署目标和资源配置。 - 更新
package.json:files:添加ios/Sources/、Package.swift;保留.podspec;移除旧的ios/Plugin/。verify:ios:xcodebuild -scheme <Name> -destination generic/platform=iOS。
验证
# Package resolves and builds standalone (from plugin root)
swift package describe >/dev/null && echo "manifest ok"
xcodebuild -scheme <Name> -destination generic/platform=iOS build
# Published tarball contains the manifest and sources
npm pack --dry-run 2>&1 | grep -E 'Package.swift|ios/Sources'
# No leftover ObjC bridge
grep -rn "CAP_PLUGIN(" ios/ && echo "LEFTOVER BRIDGE"
然后在 SPM 示例/测试应用中(包含 capacitor.config.* 的目录):安装插件(npm install ../path 或已打包的 tarball),运行 npx cap sync ios,确认没有 does not have a Package.swift 警告,确认 <Name> 出现在 ios/App/CapApp-SPM/Package.swift 中,且该类出现在 ios/App/App/capacitor.config.json 的 packageClassList 中,进行构建,并从 JS 调用一个方法。如果 podspec 仍在发布,也要运行 pod lib lint(或使用 CocoaPods 示例应用)。
错误处理
| 错误 | 修复方法 |
|---|---|
product '<Name>' required by package 'capapp-spm' target 'CapApp-SPM' not found in package '<Name>' | Package(name:) / .library(name:) 与 CLI 推导出的 <Name> 不匹配。 |
"Foo" plugin is not implemented on ios | 缺少 @objc(FooPlugin)、jsName 错误,或类不在 packageClassList 中;修复后重新同步。 |
"Foo.bar()" is not implemented on ios | 方法缺少 pluginMethods 声明,或未标记为 @objc。 |
No such module 'Capacitor' building the package | 缺少 capacitor-swift-pm 依赖,或产品名称错误。 |
public headers ("include") directory path for 'X' is invalid or not contained in the target | 混合 ObjC 目标未设置 publicHeadersPath;将 ObjC 拆分到单独的目标中(见参考资料)。 |
<id> is built for Capacitor <N>, it might cause issues (in app) | 将 capacitor-swift-pm 的 from: 升级到与应用相同的主版本。 |
Cordova product missing (Capacitor 9 app) | 移除 Cordova 产品依赖。 |
| 运行时找不到资源 | 声明 resources: 并通过 Bundle.module 而非 Bundle.main 加载。 |
参考资料
仅在涉及相关主题时加载:
references/package-swift-templates.md:基础清单、资源和隐私清单、第三方 SPM 依赖、二进制.xcframework、混合 Swift/ObjC 目标、Capacitor 8 与 9 的依赖配置块。