> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userpilot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Updating to Swift Package Manager

> Migrate your Userpilot Capacitor app from CocoaPods to Swift Package Manager, complete Xcode setup, and verify native SDK dependencies.

### Overview

Migrate your Userpilot Capacitor iOS app from CocoaPods to Swift Package Manager
(SPM). Follow these steps for apps that still use CocoaPods, including older
copies of the Userpilot Capacitor example.

Capacitor 8 uses SPM by default for new iOS projects and continues to support existing CocoaPods projects. Upgrading Capacitor or running `npx cap sync ios` does not migrate an existing CocoaPods project. See [Capacitor's version 8 upgrade guide](https://capacitorjs.com/docs/updating/8-0).

`@userpilot/capacitor` supports both dependency managers on Capacitor 6, 7, and 8. The steps below target a Capacitor 8 app.

Run commands from your app directory (`example-app/` in the Userpilot Capacitor repository).

### Use cases

* Migrate an existing CocoaPods app while preserving its native configuration.
* Set up a new Capacitor 8 iOS project with SPM and the Userpilot plugin.

## Check your current setup

For the standard Capacitor iOS directory layout:

| App file | Dependency manager | How Userpilot is installed |
| - | - | - |
| `ios/App/Podfile`, with no `ios/App/CapApp-SPM/` directory | CocoaPods | The generated plugin entry loads `UserpilotCapacitor.podspec`, which depends on the native Userpilot iOS SDK. |
| `ios/App/CapApp-SPM/Package.swift` | SPM | The generated package references the plugin's `Package.swift`, which depends on the native Userpilot iOS SDK. |

Capacitor detects SPM from the `CapApp-SPM` directory. There is no Userpilot configuration flag to switch dependency managers. If your app already uses SPM, skip the migration and follow [Verify the integration](#verify-the-integration).

## Before migrating

* Save a restorable copy of your iOS project, including uncommitted changes, signing settings, capabilities, URL schemes, custom native code, and extension targets.
* Ensure your Capacitor 8 environment meets its requirements: Node.js 22+, Xcode 26+, and an app deployment target of iOS 15+. Keep `@capacitor/core`, `@capacitor/cli`, and `@capacitor/ios` on compatible versions. See the [Capacitor 8 upgrade guide](https://capacitorjs.com/docs/updating/8-0).
* Install your app's npm dependencies and [install Userpilot](./installation#installation) if you have not already. The repository's example already declares Userpilot as a local dependency.
* Check SPM support for every installed Capacitor plugin. Userpilot supports SPM, but another plugin may require an update or replacement. Capacitor does not automatically fall back to CocoaPods for incompatible plugins. Cordova plugins also need to be checked individually.
* Review custom pods, `Podfile` hooks, build settings, and dependencies belonging to app extensions. Arrange their SPM equivalents and preserve any custom setup before the assistant removes CocoaPods integration. Keep CocoaPods available to run `pod deintegrate` during migration.

## Migrate an existing CocoaPods app

Build your web assets, then run Capacitor's migration assistant:

```sh theme={null}
npm run build
npx cap spm-migration-assistant
```

The assistant runs `pod deintegrate`, removes `ios/App/Podfile`, `Podfile.lock`, and `App.xcworkspace`, and creates `ios/App/CapApp-SPM/` and `ios/debug.xcconfig`. It generates package dependencies from your installed plugins and reports incompatible plugins. Resolve those warnings before continuing; custom pod dependencies and hooks need your own migration work.

### Complete the Xcode setup

```sh theme={null}
npx cap open ios
```

After migration, use `App.xcodeproj`; the CocoaPods workspace has been removed.

1. Select the **App** project, then **Package Dependencies**, and click **+**.
2. Choose **Add Local...**, select `ios/App/CapApp-SPM`, and add the package.
3. Add its **CapApp-SPM** library product to your **App** target. Confirm it appears in the target's **Frameworks, Libraries, and Embedded Content**.
4. Under the project's **Info > Configurations**, add `ios/debug.xcconfig` and use it for the app's Debug configuration. If you already use a custom Debug xcconfig, retain it and include the generated file using the correct relative path.
5. Restore any custom build settings or scripts previously supplied by your `Podfile`, and add any separately managed native or extension dependencies to their intended targets.

See [Capacitor's illustrated migration steps](https://capacitorjs.com/docs/ios/spm#using-our-migration-tool) for the package and configuration dialogs.

## Use SPM in a new iOS project

For a Capacitor 8 app that does not yet have an iOS project:

```sh theme={null}
npm install @capacitor/ios@8 @userpilot/capacitor
npm run build
npx cap add ios --packagemanager SPM
npx cap sync ios
npx cap open ios
```

The SPM template already includes the package and Debug configuration. Use the migration steps above for an existing iOS project so you preserve its native configuration.

## Verify the integration

From the app directory:

```sh theme={null}
npm run build
npx cap sync ios
npx cap open ios
```

Confirm that:

* `ios/App/CapApp-SPM/Package.swift` includes the `UserpilotCapacitor` package and product.
* Xcode resolves the native `Userpilot` dependency through the plugin's package.
* The app builds and launches on an iOS simulator or device, and your existing Userpilot initialization and tracking calls work.

The dependency chain is:

```text theme={null}
ios/App/CapApp-SPM/Package.swift
  -> @userpilot/capacitor/Package.swift
     -> Userpilot/ios-sdk
```

Do not add the native Userpilot SDK separately to the app target. The plugin's [Package.swift](https://github.com/Userpilot/capacitor-plugin/blob/main/Package.swift) selects its version. Capacitor regenerates `CapApp-SPM/Package.swift` during sync, so avoid editing that generated file manually. Your Userpilot JavaScript initialization and API calls remain the same.

### Troubleshooting

* **A plugin is missing from the generated package:** check the sync output for SPM compatibility warnings and install a compatible plugin version before syncing again.
* **Xcode cannot resolve a package:** confirm that `CapApp-SPM` is linked to the app target and run `npx cap sync ios`. If resolution remains stale, use **File > Packages > Reset Package Caches**, then resolve the packages again.
* **Duplicate Userpilot dependencies or symbols:** remove any extra manual Userpilot installation left in the app and keep the dependency supplied by the Capacitor plugin.
* **A custom native dependency is missing:** migrate the dependency and its build settings from your saved `Podfile`, including any separate extension targets.

## Going back to CocoaPods

Restore the migration-related iOS project changes from your saved copy, including the original `Podfile`, `Podfile.lock`, workspace, Xcode project, and custom configuration. Preserve unrelated work. Remove the generated `CapApp-SPM` directory and `ios/debug.xcconfig` if they did not exist before migration; leaving `CapApp-SPM` in place makes Capacitor continue selecting SPM.

Then reinstall the CocoaPods dependencies and open the restored workspace:

```sh theme={null}
npx cap sync ios
npx cap open ios
```

For a new iOS project, you can explicitly choose CocoaPods with `npx cap add ios --packagemanager CocoaPods`. This command does not convert an existing SPM project.

## References

* [Swift Package Manager in Capacitor](https://capacitorjs.com/docs/ios/spm)
* [Updating to Capacitor 8](https://capacitorjs.com/docs/updating/8-0)
* [Userpilot Capacitor package manifest](https://github.com/Userpilot/capacitor-plugin/blob/main/Package.swift)
* [Userpilot Capacitor CocoaPods specification](https://github.com/Userpilot/capacitor-plugin/blob/main/UserpilotCapacitor.podspec)

### FAQs

<AccordionGroup>
  <Accordion title="Do I need to change my Userpilot JavaScript calls?">
    Your initialization and tracking calls remain the same. SPM changes how
    your iOS app installs the native dependencies.
  </Accordion>

  <Accordion title="Can I install the native Userpilot package separately?">
    Keep the native SDK dependency supplied by the Capacitor plugin. Adding
    another copy to the app target can cause duplicate dependencies or symbols.
  </Accordion>
</AccordionGroup>

<Frame>
  [Contact **support@userpilot.com** for help with your integration.](mailto:support@userpilot.com)
</Frame>
