capacitor-plugin-spm-support

Guides the agent through adding Swift Package Manager support to an existing Capacitor plugin. Covers Package.swift, CAPBridgedPlugin conversion, bridge cleanup, and package manifest updates. Do not use for app projects or non-Capacitor plugin frameworks.

By cap-go · 599 installs

npx skills add cap-go/capgo-skills --skill capacitor-plugin-spm-support

Source repository · Upstream listing

Add Swift Package Manager Support to a Capacitor Plugin Make a plugin installable in SPM based Capacitor apps while keeping its podspec for CocoaPods apps. When to Use TRIGGER when: A plugin repo has ios/Plugin/ .swift + .m / .h with CAP PLUGIN(...) and no root Package.swift . An app's npx cap sync ios prints <plugin id does not have a Package.swift for a plugin the user maintains. A plugin works with CocoaPods but JS gets "X" plugin is not implemented on ios in SPM apps. Preparing a plugin for Capacitor 9 / CocoaPods Trunk read only. Do not use: App side CocoaPods removal cocoapods to spm . Capacitor major bumps of a plugin capacitor plugin upgrades , capacitor plugin upgrade v8 to v9 . Android only plugin work, or non Capacitor (pure Cordova, React Native) plugins. Rules the CLI Enforces (non obvious) Package.swift must be at the npm package root (next to package.json ), not under ios/ . The app CLI only checks <plugin root /Package.swift . The CLI references the plugin in the app's CapApp SPM/Package.swift as .package(name: "<Name ", path: ...) and .product(name: "<Name ", package: "<Name ") , where <Name is derived from the npm name: drop @ , turn / and into word breaks, PascalCase. @capgo/capacitor updater CapgoCapacitorUpdater , @acme/capacitor foo bar AcmeCapacitorFooBar . The Package(name:) and the .library(name:) product must both equal <Name , or app builds fail to resolve the product. Runtime registration uses ios/App/App/capacitor.config.json packageClassList , filled by scanning the plugin's iOS sources ( capacitor.ios.src in package.json, default ios ) for @objc(ClassName) . Keep @objc(<Class Plugin) on the class; without it the plugin is never registered. The capacitor swift pm dependency must use from: "<major .0.0" . If the major differs from the app's Capacitor, app sync rewrites it in node modules and warns <id is built for Capacitor <N , it might cause issues . Capacitor 9: depend only on .product(name: "Capacitor", package: "capacitor swift pm") . Remove the unconditional Cordova product; it is only present in apps that install a Cordova plugin. Keep it on Capacitor 8 only if your code imports Cordova. Procedure 1. Inspect Read package.json (name, files , capacitor.ios.src , scripts), the .podspec ( s.name , s.ios.deployment target , s.dependency , s.resources , s.vendored frameworks ), every file in ios/ , and the CAP PLUGIN(...) macro in the .m file. Record: SPM <Name (rule above), class name, jsName , every CAP PLUGIN METHOD(name, returnType) , iOS deployment target, third party pods, resources, vendored frameworks, ObjC/C sources other than the bridge. Check each third party pod has an official SPM package. Report any that do not before editing; ask whether to drop, vendor as .xcframework , or keep the plugin CocoaPods only. 2. Fast path: converter If the iOS code is Swift only apart from the [Name]Plugin.m / .h bridge, the official converter does most of the work (adds CAPBridgedPlugin , creates Package.swift , moves code to ios/Sources and ios/Tests , removes Plugin.xcodeproj / xcworkspace / Podfile , updates podspec source files , files , and verify:ios ): https://github.com/ionic team/capacitor plugin converter. Review its diff against the rules above, especially the package/product name. 3. Manual conversion 1. Move sources: ios/Plugin/ ios/Sources/<ClassName / , tests ios/Tests/<ClassName Tests/ . 2. Convert the class (keep method names and return types exactly as in the .m macro): 3. Delete FooPlugin.m and FooPlugin.h (the CAP PLUGIN bridge) and stale Plugin.xcodeproj , Plugin.xcworkspace , Podfile , Info.plist files under ios/ . 4. Add root Package.swift . Template and variants (resources, binary targets, third party packages, mixed ObjC) in references/package swift templates.md . 5. Update the podspec: s.source files = 'ios/Sources/ / .{swift,h,m,c,cc,mm,cpp}' , keep s.dependency 'Capacitor' , and mirror deployment target and resources. 6. Update package.json : files : add ios/Sources/ , Package.swift ; keep the .podspec ; remove old ios/Plugin/ . verify:ios : xcodebuild scheme <Name destination generic/platform=iOS . Verification Then in an SPM example/test app (directory with capacitor.config. ): install the plugin ( npm install ../path or a packed tarball), run npx cap sync ios , confirm no does not have a Package.swift warning, confirm <Name appears in ios/App/CapApp SPM/Package.swift and the class appears in ios/App/App/capacitor.config.json packageClassList , build, and call one method from JS. Also run pod lib lint (or a CocoaPods example app) if the podspec is still published. Error Handling Error Fix product '<Name ' required by package 'capapp spm' target 'CapApp SPM' not found in package '<Name ' Package(name:) / .library(name:) do not match the CLI derived <Name . "Foo" plugin is not implemented on ios Missing @objc(FooPlugin) , wrong jsName , or class not in packageClassList ; re sync after fixing. "Foo.bar()" is not implemented on ios Method missing from pluginMethods or not marked @objc . No such module 'Capacitor' building the package Missing capacitor swift pm dependency or wrong product name. public headers ("include") directory path for 'X' is invalid or not contained in the target Mixed ObjC target without publicHeadersPath ; split ObjC into its own target (see reference). <id is built for Capacitor <N , it might cause issues (in app) Bump capacitor swift pm from: to the app's major. Cordova product missing (Capacitor 9 app) Remove the Cordova product dependency. Resource not found at runtime Declare resources: and load via Bundle.module , not Bundle.main . References Only load when the topic is in play: references/package swift templates.md : base manifest, resources and privacy manifest, third party SPM dependencies, binary .xcframework , mixed Swift/ObjC targets, Capacitor 8 vs 9 dependency blocks.