capawesome-team/skills

capacitor-app-spm-migration

Guides the agent through migrating an existing Capacitor app project from CocoaPods to Swift Package Manager (SPM) for iOS dependency management.

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Capacitor App: CocoaPods → SPM Migration

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.

Prerequisites

RequirementVersion
Capacitor6+
Node.js18+
Xcode15+
CocoaPodsinstalled (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).

Procedures

Step 1: Verify Project State

  1. Verify the project is a Capacitor app, not a plugin. Confirm package.json lists @capacitor/ios in dependencies and the project root contains a capacitor.config.* file.
  2. Verify the project currently uses CocoaPods. The 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.
  3. Read @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.
  4. Verify the working tree is clean. Run 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.

Step 2: Inventory Installed Capacitor Plugins

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/*
  • Any other package with "capacitor" in its package.json keywords array

Record 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.

Step 3: Identify Files to Preserve

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.plistApp permissions, URL schemes, bundle config, custom keys
ios/App/App/AppDelegate.swiftCustom app lifecycle code
ios/App/App/SceneDelegate.swiftCustom 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.entitlementsPush, App Groups, Associated Domains, Keychain Sharing
ios/App/App/GoogleService-Info.plistFirebase configuration (if Firebase plugins are installed)
ios/App/App/*.xcconfigBuild configuration overrides
ios/App/App.xcodeproj/project.pbxprojSource 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.

Step 4: Back Up Preserved Files

  1. Create the backup directory:
bash
   mkdir -p .spm-migration-backup
  1. Add .spm-migration-backup/ to .gitignore to prevent accidental commits:
diff
    # iOS files
   +.spm-migration-backup/
  1. Copy each file recorded in Step 3 into .spm-migration-backup/, preserving the relative path. For example:
bash
   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.

  1. 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.

Step 5: Extract Signing & Bundle Configuration

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_IDENTIFIER
  • DEVELOPMENT_TEAM
  • CODE_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_VERSION
  • CURRENT_PROJECT_VERSION
  • IPHONEOS_DEPLOYMENT_TARGET
  • SWIFT_VERSION

Also 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.

Step 6: Delete the ios/ Folder

Confirm 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:

bash
rm -rf ios

Step 7: Re-scaffold iOS with SPM

Run:

bash
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.

Step 8: Restore Preserved Files

Copy each backed-up file from .spm-migration-backup/ back to its original path under ios/, overwriting the scaffolded defaults. For example:

bash
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.

Step 9: Reapply Signing & Bundle Configuration

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):

diff
-				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_IDENTIFIER
  • DEVELOPMENT_TEAM
  • CODE_SIGN_STYLE
  • PROVISIONING_PROFILE_SPECIFIER (only if manual signing)
  • CODE_SIGN_IDENTITY (only if manual signing)
  • MARKETING_VERSION
  • CURRENT_PROJECT_VERSION

If 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).

Step 10: Add Restored Custom Files to the Xcode Project

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):

  1. Open the project: npx cap open ios (or open ios/App/App.xcodeproj).
  2. In the Xcode Project Navigator, right-click the App group and choose Add Files to "App"….
  3. Select each restored custom file (e.g., App.entitlements, GoogleService-Info.plist, custom .swift files).
  4. Ensure Copy items if needed is unchecked and the 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:

diff
 				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.

Step 11: Reconfigure Capabilities

For each capability key recorded from App.entitlements in Step 5, verify the corresponding capability is enabled in Xcode:

  1. Open ios/App/App.xcodeproj via npx cap open ios.
  2. Select the App target → Signing & Capabilities tab.
  3. For each entitlement key, add the matching capability via the + Capability button:
  • aps-environmentPush Notifications
  • com.apple.security.application-groupsApp Groups
  • com.apple.developer.associated-domainsAssociated Domains
  • keychain-access-groupsKeychain Sharing
  • com.apple.developer.in-app-paymentsApple Pay
  • com.apple.developer.icloud-servicesiCloud

This is the only step that requires Xcode interaction. Inform the user explicitly that this step must be done manually.

Step 12: Re-sync Plugins

Run:

bash
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:

  • Wait for upstream SPM support from the plugin maintainer, or
  • Fork the plugin and add SPM support using the capacitor-plugin-spm-support skill, or
  • Remove the plugin from the project.

Step 13: Verify the Build

  1. Open the project in Xcode and resolve SPM packages:
bash
   npx cap open ios

In Xcode, File → Packages → Resolve Package Versions (or wait for automatic resolution on open).

  1. Build for a simulator from the command line to confirm everything compiles:
bash
   npx cap run ios
  1. Verify the app launches and that previously working features (push notifications, deep links, Firebase, etc.) still function.

Step 14: Cleanup

Once the build is verified and the app runs correctly:

  1. Delete the backup directory:
bash
   rm -rf .spm-migration-backup
  1. Remove the .spm-migration-backup/ entry from .gitignore.
  1. Verify no CocoaPods artifacts remain:
bash
   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.

  1. Commit the migration:
bash
   git add -A
   git commit -m "chore(ios): migrate from CocoaPods to Swift Package Manager"

Error Handling

  • `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.
  • Build fails with "Module 'X' not found" — the plugin's SPM package was not resolved. Run File → Packages → Reset Package Caches in Xcode, then File → Packages → Resolve Package Versions.
  • Build fails with code signing errors — the values reapplied in Step 9 are missing or incorrect. Re-read .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.
  • Push notifications stop working after migration — the Push Notifications capability was not re-added in Step 11, or App.entitlements is not linked via CODE_SIGN_ENTITLEMENTS (Step 10).
  • Firebase initializes but no data appearsGoogleService-Info.plist was restored to disk but not added to the Xcode project's target membership in Step 10.
  • Custom Notification Service Extension target is missing — non-App targets are not regenerated by npx cap add ios. Recreate the target manually in Xcode and re-add its source files.
  • The user reports they want to revert — because Step 1 verified a clean working tree, the entire migration can be undone with git checkout -- . followed by git clean -fd ios .spm-migration-backup.

Related Skills

  • `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.
from this repository

More skills

All skills
capawesome-team
Community

capacitor-angular

Guides the agent through Angular-specific patterns for Capacitor app development. Covers project structure, adding Capacitor to Angular projects, using Capacitor plugins in Angular services and components, NgZone integration for plugin event listeners, lifecycle hook patterns, dependency injection, routing with deep links, and environment-based platform detection. Do not use for creating a new Capacitor app from scratch, upgrading Capacitor versions, installing specific plugins, Ionic Framework setup, or non-Angular frameworks.

installs
4
GitHub stars
45
Updated
Sep 13
capawesome-team
Community

capacitor-app-creation

Guides the agent through creating a new Capacitor app from scratch. Covers project scaffolding with the Capacitor CLI, configuring the app (appId, appName, webDir), adding native platforms (iOS, Android), and syncing. Includes decision points for Ionic Framework integration, live updates, and CI/CD setup. Do not use for upgrading existing Capacitor apps, migrating from other frameworks, or plugin installation.

installs
4
GitHub stars
45
Updated
Sep 13
capawesome-team
Community

capacitor-app-development

Guides the agent through general Capacitor app development topics. Covers core concepts (native bridge, plugins, web layer), Capacitor CLI usage, app configuration (capacitor.config.ts, splash screens, app icons, deep links), platform management (Android, iOS, Electron, PWA), edge-to-edge and safe area handling on Android, live reload setup, storage solutions, file handling, security best practices, CI/CD references, iOS package managers (SPM, CocoaPods), and troubleshooting for Android and iOS. Do not use for creating new Capacitor apps, Capacitor plugin APIs, creating Capacitor plugins, in-app purchases, upgrading Capacitor versions, Cordova or PhoneGap migration, or framework-specific patterns (Angular, React, Vue).

installs
4
GitHub stars
45
Updated
Sep 13
capawesome-team
Community

capacitor-app-upgrades

Guides the agent through upgrading a Capacitor app project to a newer major version. Supports upgrades from Capacitor 4 through 8, including multi-version jumps. Performs all upgrade steps manually — does not use the interactive npx cap migrate command. Do not use for plugin library upgrade or non-Capacitor mobile frameworks.

installs
4
GitHub stars
45
Updated
Sep 13