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 {
capacitor-plugin-converter
2. Use 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.
new-template
with Existing Plugin Code
4.1. Overwrite Move the existing plugin code (ios/plugins
contents) to the new-template/ios/Sources/**Plugin
directory.
ios
Folder with the new-template/ios
Folder
4.2. Overwrite the Existing 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.
Package.swift
with new-template/Package.swift
4.4. Overwrite the Top-Level 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.
new-template
4.5. Delete Delete the new-template
directory; it's no longer needed.
**.podspec
File
4.6. Update the 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}'
Package.swift
5. Update 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