5 Steps to Migrate Capacitor Plugin to SPM Support
Let's migrate your Capacitor iOS plugin to support Swift Package Manager (SPM) in addition to CocoaPods. Read more about the benefits of SPM here: https://zenn.dev/rdlabo/articles/4456315c9ca829(This is Japanese Article)
1. Rename Plugin Files
The "plugin files" here refer to the header and m files located under ios/Plugin, and Swift files prefixed with @objc that are called first as plugins. Older plugin structures look like this:
- Plugin.swift
- Plugin.m
- Plugin.h
This is confusing. Rename your files to the more modern structure below. Replace ** with your plugin name; for example, for an AdMob plugin, **Plugin.swift would become AdMobPlugin.swift.
- **Plugin.swift
- **Plugin.m
- **Plugin.h
Note: You'll eventually delete files like Plugin.xcodeproj, so you don't need to rename them in Xcode. Simply rename the files themselves. Now open **Plugin.swift. If the class name isn't **Plugin, change it:
- @objc(Stripe)
- public class Plugin: CAPPlugin {
+ @objc(StripePlugin)
+ public class StripePlugin: CAPPlugin {
2. Use capacitor-plugin-converter
Use the capacitor-plugin-converter tool to automate parts of the conversion. This is straightforward: download the zip file using the following command:
% curl -OL https://github.com/ionic-team/capacitor-plugin-converter/releases/latest/download/cap2spm.zip
Double-click to extract the Unix executable. Let's say you downloaded it to the same directory as your payment plugin, which is located in the ./payment folder. (In this example, ** in **.swift is PaymentPlugin.) Run this command:
% ./cap2spm payment --no-backup
The --no-backup flag prevents renaming existing files with a .old extension. This is usually unnecessary if you're using Git. This command deletes **Plugin.h and **Plugin.m, moving their contents into the pluginMethods property of **Plugin.swift. Expect additions similar to this:
SPM doesn't read header and m files, so this migration is necessary. A Package.swift file will also be automatically generated in the top directory.
3. Create a New Plugin within the Plugin
After considering various migration methods, creating a new plugin and copying the ios folder is the least problematic. Here's how to create a new plugin:
% cd payment # Navigate to your plugin folder (e.g., 'payment')
% npm init @capacitor/plugin@latest -- --package-id com.hoge.huga --repo https://example.com --author "hoge" --license MIT --description "hoge" ## Create a new plugin to copy into
For simplicity, I've adjusted the command to allow for placeholder values for unnecessary fields. You'll be prompted to fill in the following:
✔ What should be the npm package of your plugin?
… Enter your current plugin's npm name.
✔ What directory should be used for your plugin?
… Plugin folder name. Let's use `new-template`.
✔ What should be the class name for your plugin?
… Enter the "**" part of `**Plugin.swift`.
npm install will run after file generation, but you can safely interrupt it.
4. Refresh the iOS Folder
Now, let's refresh the structure. We'll use parts of the new-template directory and keep what's needed from the existing structure. Remember, we're not changing the web and Android code, so avoid accidentally deleting or overwriting it.
4.1. Overwrite new-template with Existing Plugin Code
Move the existing plugin code (ios/plugins contents) to the new-template/ios/Sources/**Plugin directory.
4.2. Overwrite the Existing ios Folder with the new-template/ios Folder
The existing ios folder is now redundant. Because the structure has significantly changed, instead of deleting unnecessary files and readjusting paths, let's create a cleaner structure for the future.
Delete the existing ios folder and place the new-template/ios folder in its place.
4.4. Overwrite the Top-Level Package.swift with new-template/Package.swift
Overwrite the automatically generated Package.swift from step 2 with the one from the newly created plugin. In most cases, the newly generated Package.swift provides a cleaner structure than the one created by the migration tool.
4.5. Delete new-template
Delete the new-template directory; it's no longer needed.
4.6. Update the **.podspec File
Update the podspec file to reflect the change in the iOS directory path:
- s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
5. Update Package.swift
If you have dependencies, you need to update Package.swift. For example, let's say your podspec has these dependencies:
s.dependency 'StripePaymentSheet', '~> 23.32.0'
s.dependency 'StripeApplePay', '~> 23.32.0'
You need to add them to Package.swift as well:
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", branch: "main"),
+ .package(url: "https://github.com/stripe/stripe-ios-spm.git", branch: "main")
],
targets: [
.target(
name: "StripePlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
+ .product(name: "StripePaymentSheet", package: "stripe-ios-spm"),
+ .product(name: "StripeApplePay", package: "stripe-ios-spm")
],
path: "ios/Sources/StripePlugin"),
Remember that you'll need to update both the podspec and Package.swift files to manage packages as you'll be using two package managers simultaneously.
That completes the migration!
Summary
While it would be ideal if capacitor-plugin-converter handled more of this automatically, the evolution of Capacitor plugin structures over time necessitates these steps. Copying and moving folders might seem complicated, but with a clear plan of the migration steps, it becomes a straightforward process. Give it a try!
Discussion