skills/capacitor-app-spm-migration/SKILL.md
Guides the agent through migrating an existing Capacitor app project from CocoaPods to Swift Package Manager (SPM) for iOS dependency management. Covers prerequisite checks, inventorying installed Capacitor plugins, backing up customized iOS project files (Info.plist, AppDelegate.swift, Assets.xcassets, Base.lproj, App.entitlements, GoogleService-Info.plist, .xcconfig files, signing configuration), deleting the existing `ios/` folder, re-scaffolding with `npx cap add ios --packagemanager SPM`, restoring preserved files, re-syncing plugins, and verifying the build. Performs all migration steps manually — does not use the interactive `npx cap spm-migration-assistant` command. Do not use for Capacitor plugin projects, app projects already on SPM, app projects without an existing `ios/` folder, or non-Capacitor mobile frameworks.
npx skillsauth add capawesome-team/skills capacitor-app-spm-migrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Migrate an existing Capacitor app project's iOS dependency management from CocoaPods to Swift Package Manager (SPM). The migration is destructive — the existing ios/ folder is deleted and re-scaffolded, then user-authored files are restored from a temporary backup.
| Requirement | Version | | ----------- | ------- | | Capacitor | 6+ | | Node.js | 18+ | | Xcode | 15+ | | CocoaPods | installed (only used for verification before deletion) |
The project must be a Capacitor app (not a plugin) and must currently use CocoaPods (ios/App/Podfile exists).
package.json lists @capacitor/ios in dependencies and the project root contains a capacitor.config.* file.ios/App/Podfile must exist. If it does not exist and ios/App/CapApp-SPM/Package.swift exists, the project is already on SPM — abort and inform the user.@capacitor/core version from package.json (dependencies or devDependencies). Confirm the major version is 6 or higher. If lower, abort and direct the user to the capacitor-app-upgrades skill first.git status --porcelain — if the output is non-empty, instruct the user to commit or stash changes before proceeding. The migration is destructive and must be reversible via git checkout.Read package.json and record every installed Capacitor plugin under dependencies. Match these prefixes:
@capacitor/* (official plugins)@capawesome/* and @capawesome-team/*@capacitor-community/*@capacitor-firebase/*@capacitor-mlkit/*@revenuecat/*"capacitor" in its package.json keywords arrayRecord each plugin's package name and installed version. This list is used in Step 8 to verify all plugins resolve under SPM after re-sync.
Build a checklist of files in ios/ that contain user-authored content and must survive the re-scaffold. For each path below, check whether the file exists. Record only the paths that exist.
| Path (relative to project root) | Purpose |
| ------------------------------- | ------- |
| ios/App/App/Info.plist | App permissions, URL schemes, bundle config, custom keys |
| ios/App/App/AppDelegate.swift | Custom app lifecycle code |
| ios/App/App/SceneDelegate.swift | Custom scene lifecycle code (if present) |
| ios/App/App/Assets.xcassets/ | App icon, splash assets, color sets |
| ios/App/App/Base.lproj/ | LaunchScreen and Main storyboards |
| ios/App/App/App.entitlements | Push, App Groups, Associated Domains, Keychain Sharing |
| ios/App/App/GoogleService-Info.plist | Firebase configuration (if Firebase plugins are installed) |
| ios/App/App/*.xcconfig | Build configuration overrides |
| ios/App/App.xcodeproj/project.pbxproj | Source for extracting signing values in Step 5 |
Additionally, scan ios/App/App/ for any other .swift files beyond AppDelegate.swift and SceneDelegate.swift. These are user-authored Swift sources (e.g., custom Notification Service Extensions, custom view controllers) and must also be preserved.
If the project contains a Notification Service Extension or other Xcode targets under ios/App/ (e.g., ios/App/NotificationService/), record those directories as well.
Create the backup directory:
mkdir -p .spm-migration-backup
Add .spm-migration-backup/ to .gitignore to prevent accidental commits:
# iOS files
+.spm-migration-backup/
Copy each file recorded in Step 3 into .spm-migration-backup/, preserving the relative path. For example:
mkdir -p .spm-migration-backup/ios/App/App
cp ios/App/App/Info.plist .spm-migration-backup/ios/App/App/Info.plist
cp ios/App/App/AppDelegate.swift .spm-migration-backup/ios/App/App/AppDelegate.swift
cp -R ios/App/App/Assets.xcassets .spm-migration-backup/ios/App/App/Assets.xcassets
cp -R ios/App/App/Base.lproj .spm-migration-backup/ios/App/App/Base.lproj
Repeat for every file/directory identified in Step 3. Use cp -R for directories.
Always copy the full ios/App/App.xcodeproj/project.pbxproj to .spm-migration-backup/ios/App/App.xcodeproj/project.pbxproj, even though the file itself will not be restored. Step 5 reads it to extract signing values, and Step 11 may reference it for capability reconfiguration.
Read .spm-migration-backup/ios/App/App.xcodeproj/project.pbxproj and extract the values of the following build settings from the App target's XCBuildConfiguration blocks (both Debug and Release). Search for each key and record the assigned value:
PRODUCT_BUNDLE_IDENTIFIERDEVELOPMENT_TEAMCODE_SIGN_STYLE (e.g., Automatic or Manual)PROVISIONING_PROFILE_SPECIFIER (only if CODE_SIGN_STYLE = Manual)CODE_SIGN_IDENTITY (only if CODE_SIGN_STYLE = Manual)MARKETING_VERSIONCURRENT_PROJECT_VERSIONIPHONEOS_DEPLOYMENT_TARGETSWIFT_VERSIONAlso record the list of capabilities by reading .spm-migration-backup/ios/App/App/App.entitlements (if present). Note all top-level keys (e.g., aps-environment, com.apple.security.application-groups, com.apple.developer.associated-domains).
Save the extracted values in memory for use in Step 9.
ios/ FolderConfirm with the user that the working tree is committed (re-run git status --porcelain if necessary) and that .spm-migration-backup/ contains every file from Step 3. Then run:
rm -rf ios
Run:
npx cap add ios --packagemanager SPM
This creates a new ios/ directory with the SPM-based scaffold. The new structure includes ios/App/CapApp-SPM/Package.swift instead of ios/App/Podfile.
Copy each backed-up file from .spm-migration-backup/ back to its original path under ios/, overwriting the scaffolded defaults. For example:
cp .spm-migration-backup/ios/App/App/Info.plist ios/App/App/Info.plist
cp .spm-migration-backup/ios/App/App/AppDelegate.swift ios/App/App/AppDelegate.swift
cp -R .spm-migration-backup/ios/App/App/Assets.xcassets ios/App/App/Assets.xcassets
cp -R .spm-migration-backup/ios/App/App/Base.lproj ios/App/App/Base.lproj
Repeat for every file/directory backed up in Step 4, except ios/App/App.xcodeproj/project.pbxproj — that file is not restored. The new project.pbxproj is required for SPM integration; signing values from the old one are reapplied via Step 9.
If any preserved file did not exist in the new scaffold (e.g., App.entitlements, GoogleService-Info.plist, custom Swift files), the file must additionally be added to the Xcode project membership in Step 10.
Edit the new ios/App/App.xcodeproj/project.pbxproj and reapply the values extracted in Step 5. For each setting, find every occurrence in the App target's XCBuildConfiguration blocks and replace the scaffolded value with the recorded value.
Apply this diff pattern for each setting (example for PRODUCT_BUNDLE_IDENTIFIER):
- PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
+ PRODUCT_BUNDLE_IDENTIFIER = <RECORDED_BUNDLE_ID>;
Settings to reapply (use replace_all semantics — update all occurrences across Debug and Release configurations):
PRODUCT_BUNDLE_IDENTIFIERDEVELOPMENT_TEAMCODE_SIGN_STYLEPROVISIONING_PROFILE_SPECIFIER (only if manual signing)CODE_SIGN_IDENTITY (only if manual signing)MARKETING_VERSIONCURRENT_PROJECT_VERSIONIf IPHONEOS_DEPLOYMENT_TARGET in the recorded values is higher than the scaffolded value, update it. If lower, keep the scaffolded value (the new scaffold targets a supported minimum).
The new project.pbxproj only references files that the scaffold created. Files restored in Step 8 that did not exist in the scaffold (e.g., App.entitlements, GoogleService-Info.plist, custom Swift files, Notification Service Extension targets) are present on disk but invisible to Xcode until they are added to the project's PBXBuildFile, PBXFileReference, and PBXGroup entries.
For each custom file that was not part of the scaffold, instruct the user to perform the following one-time action in Xcode (the SPM scaffold has no App.xcworkspace — open App.xcodeproj directly):
npx cap open ios (or open ios/App/App.xcodeproj).App group and choose Add Files to "App"….App.entitlements, GoogleService-Info.plist, custom .swift files).App target is checked.For the App.entitlements file specifically, also set the CODE_SIGN_ENTITLEMENTS build setting in ios/App/App.xcodeproj/project.pbxproj. Apply this diff pattern in both Debug and Release XCBuildConfiguration blocks for the App target:
CODE_SIGN_STYLE = Automatic;
+ CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CURRENT_PROJECT_VERSION = 1;
If the project had additional Xcode targets (e.g., a Notification Service Extension under ios/App/NotificationService/), the user must recreate those targets in Xcode. SPM scaffolding does not regenerate non-App targets. Inform the user explicitly.
For each capability key recorded from App.entitlements in Step 5, verify the corresponding capability is enabled in Xcode:
ios/App/App.xcodeproj via npx cap open ios.App target → Signing & Capabilities tab.aps-environment → Push Notificationscom.apple.security.application-groups → App Groupscom.apple.developer.associated-domains → Associated Domainskeychain-access-groups → Keychain Sharingcom.apple.developer.in-app-payments → Apple Paycom.apple.developer.icloud-services → iCloudThis is the only step that requires Xcode interaction. Inform the user explicitly that this step must be done manually.
Run:
npm install
npx cap sync ios
npx cap sync ios updates ios/App/CapApp-SPM/Package.swift to include every Capacitor plugin recorded in Step 2. Watch for warnings — any plugin without SPM support will be reported here.
If a plugin is reported as incompatible with SPM, inform the user. The user must either:
capacitor-plugin-spm-support skill, orOpen the project in Xcode and resolve SPM packages:
npx cap open ios
In Xcode, File → Packages → Resolve Package Versions (or wait for automatic resolution on open).
Build for a simulator from the command line to confirm everything compiles:
npx cap run ios
Verify the app launches and that previously working features (push notifications, deep links, Firebase, etc.) still function.
Once the build is verified and the app runs correctly:
Delete the backup directory:
rm -rf .spm-migration-backup
Remove the .spm-migration-backup/ entry from .gitignore.
Verify no CocoaPods artifacts remain:
find ios -name 'Podfile*' -o -name 'Pods' -o -name '*.xcworkspace'
If any results appear, delete them. The SPM workflow uses App.xcodeproj directly — there is no App.xcworkspace.
Commit the migration:
git add -A
git commit -m "chore(ios): migrate from CocoaPods to Swift Package Manager"
npx cap add ios --packagemanager SPM fails with "ios platform already exists" — Step 6 was not completed. Re-run rm -rf ios and retry.npx cap sync ios reports a plugin without SPM support — that plugin cannot be used under SPM until it ships a Package.swift. Inform the user and pause the migration; do not silently drop the plugin..spm-migration-backup/ios/App/App.xcodeproj/project.pbxproj and confirm DEVELOPMENT_TEAM and PRODUCT_BUNDLE_IDENTIFIER were applied to both Debug and Release configurations.App.entitlements is not linked via CODE_SIGN_ENTITLEMENTS (Step 10).GoogleService-Info.plist was restored to disk but not added to the Xcode project's target membership in Step 10.App targets are not regenerated by npx cap add ios. Recreate the target manually in Xcode and re-add its source files.git checkout -- . followed by git clean -fd ios .spm-migration-backup.capacitor-plugin-spm-support — For adding SPM support to a Capacitor plugin (not an app). Use this if Step 12 reports a plugin without SPM support and the user controls that plugin's source.capacitor-plugins — For installing or reconfiguring Capacitor plugins after the migration.capacitor-app-upgrades — If the project is on Capacitor 5 or earlier, upgrade it to Capacitor 6+ first using this skill, then return here.capacitor-app-development — For general Capacitor app development topics, troubleshooting, and best practices after the migration.tools
Guides the agent through migrating Capacitor apps from discontinued Ionic Enterprise SDK plugins (Auth Connect, Identity Vault, Secure Storage) to their Capawesome alternatives (OAuth, Vault, Biometrics, Secure Preferences, SQLite). Covers dependency detection, side-by-side API mapping, code replacement, and platform-specific configuration for each plugin pair. Do not use for migrating Capacitor apps or plugins to a newer version, setting up Capawesome Cloud, or non-Capacitor mobile frameworks.
tools
Guides the agent through installing, configuring, and using Capacitor plugins from six sources — official Capacitor plugins, Capawesome plugins, Capacitor Community plugins, Capacitor Firebase plugins, Capacitor MLKit plugins, and RevenueCat plugins. Covers installation, platform-specific configuration (Android and iOS), and basic usage examples. Do not use for migrating Capacitor apps or plugins to a newer version, setting up Capacitor Live Updates, or non-Capacitor mobile frameworks.
tools
Guides the agent through Ionic Vue development patterns — project structure, Vue-specific Ionic components (IonPage, IonRouterOutlet, IonTabs), navigation with Vue Router and useIonRouter, Ionic lifecycle hooks (onIonViewWillEnter, onIonViewDidEnter, onIonViewWillLeave, onIonViewDidLeave), composable utilities (useIonRouter, useBackButton, useKeyboard), tab-based routing, lazy loading, platform detection with isPlatform, and troubleshooting common Vue-specific issues. Do not use for general Ionic component theming or CLI usage (use ionic-app-development), creating a new Ionic app (use ionic-app-creation), Capacitor-specific Vue patterns without Ionic (use capacitor-vue), upgrading Ionic versions (use ionic-app-upgrades), or non-Vue frameworks like Angular or React.
development
Guides the agent through Ionic Framework development with React — project structure, React-specific Ionic components, IonReactRouter and navigation patterns, Ionic lifecycle hooks (useIonViewWillEnter, useIonViewDidEnter, useIonViewWillLeave, useIonViewDidLeave), state management integration, and React-specific best practices for Ionic apps. Do not use for plain Capacitor React apps without Ionic (use capacitor-react), Ionic with Angular or Vue, creating a new Ionic app (use ionic-app-creation), upgrading Ionic to a newer version (use ionic-app-upgrades), or general Ionic component usage without React-specific context (use ionic-app-development).