---
title: How to Use SPM Package Traits in Capacitor 8
description: Learn how to use Swift Package Manager (SPM) package traits in Capacitor 8 to conditionally enable optional plugin dependencies like SQLCipher.
date:
  created: 2026-03-25
  updated: 2026-07-17
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor SQLite: sdks/capacitor/sqlite.md
faq: true
---

# How to Use SPM Package Traits in Capacitor 8

Capacitor 8 now supports Swift Package Manager (SPM) package traits, bringing feature-flag-like capabilities to your iOS plugin dependencies. If you've ever needed to toggle an optional dependency — like enabling SQLCipher encryption for a SQLite plugin — you can now do it directly from your Capacitor config. This was previously only possible with CocoaPods subspecs, and since SPM is the default package manager in Capacitor, this fills an important gap.

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## What Are SPM Package Traits?

Package traits were introduced in [SE-0450](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0450-swiftpm-package-traits.md){:target="_blank"}, a Swift Evolution proposal that shipped with Swift 6.1. In short, traits act as feature flags for Swift packages. They let package authors:

- **Conditionally compile code** using `#if TraitName` directives
- **Toggle optional dependencies** so consumers only pull in what they need
- **Define default traits** that activate automatically unless explicitly disabled

If you're familiar with CocoaPods subspecs or Cargo features in Rust, traits serve a similar purpose. A plugin can declare a set of traits, and the consumer picks which ones to enable.

For example, a SQLite package might define a default `SQLite` trait that uses the system-provided SQLite, and an optional `SQLCipher` trait that swaps in an encrypted variant. The consumer decides at build time which variant they want — without the plugin author needing to maintain separate packages.

## Why This Matters for Capacitor

When Capacitor moved to SPM as the default package manager (starting with Capacitor 8), plugin authors lost the ability to offer optional dependency variants. CocoaPods had subspecs for this, but SPM had no equivalent — until now.

Without trait support, a plugin author who wanted to offer an optional feature like SQLCipher encryption had two choices: bundle the dependency unconditionally (increasing binary size and adding compliance concerns), or maintain a separate plugin package entirely. Neither option was great.

With Capacitor CLI 8.3.0+, the generated `Package.swift` can now include trait information, letting you opt in to exactly the features you need.

## How It Works

Capacitor introduces two new configuration options under the `experimental` namespace in your `capacitor.config.json`:

1. **`experimental.ios.spm.swiftToolsVersion`** — Sets the `swift-tools-version` in the generated `Package.swift`. Must be `"6.1"` or higher for traits to work.
2. **`experimental.ios.spm.packageTraits`** — Maps plugin package names to arrays of trait names you want to enable.

Here's what the configuration looks like in practice:

```json title="capacitor.config.json"
{
  "appId": "com.example.app",
  "appName": "MyApp",
  "experimental": {
    "ios": {
      "spm": {
        "swiftToolsVersion": "6.1",
        "packageTraits": {
          "@capawesome-team/capacitor-sqlite": ["SQLCipher"]
        }
      }
    }
  }
}
```

After running `npx cap sync`, the Capacitor CLI generates a `CapApp-SPM/Package.swift` that includes `traits: ["SQLCipher"]` in the dependency line for the SQLite plugin and uses `swift-tools-version: 6.1` at the top of the file.

## Real-World Example: The SQLite Plugin

The [Capacitor SQLite plugin](../../sdks/capacitor/sqlite.md) is one of the first Capacitor plugins to use SPM package traits. It offers optional [SQLCipher](https://www.zetetic.net/sqlcipher/){:target="_blank"} encryption support that you can enable via a trait.

The plugin's `Package.swift` defines two traits:

```swift title="Package.swift"
traits: [
    .default(enabledTraits: ["SQLite"]),
    .trait(
        name: "SQLite",
        description: "Uses the system-provided SQLite."
    ),
    .trait(
        name: "SQLCipher",
        description: "Enables SQLCipher encryption support."
    )
]
```

When the `SQLCipher` trait is enabled, the plugin conditionally includes the SQLCipher dependency and sets a compile-time flag:

```swift title="Package.swift"
targets: [
    .target(
        name: "SqlitePlugin",
        dependencies: [
            // ...
            .product(name: "SQLCipher", package: "SQLCipher.swift",
                     condition: .when(traits: ["SQLCipher"]))
        ],
        swiftSettings: [
            .define("CAPAWESOME_INCLUDE_SQLCIPHER",
                    .when(traits: ["SQLCipher"]))
        ])
]
```

In the Swift source code, the plugin uses `#if` directives to guard encryption-specific functionality:

```swift
#if CAPAWESOME_INCLUDE_SQLCIPHER
try connection.key(encryptionKey)
#else
throw CustomError.unavailable
#endif
```

This way, apps that don't need encryption get a smaller binary without the SQLCipher dependency, while apps that do need it simply enable the trait in their Capacitor config.

## For Plugin Authors

If you maintain a Capacitor plugin and want to add trait support, here's what you need to do in your `Package.swift`:

**1. Set the Swift tools version to 6.1:**

```swift
// swift-tools-version: 6.1
```

**2. Define your traits:**

```swift
traits: [
    .default(enabledTraits: ["Default"]),
    .trait(name: "Default", description: "Standard behavior."),
    .trait(name: "OptionalFeature", description: "Enables an optional feature.")
]
```

**3. Add conditional dependencies:**

```swift
dependencies: [
    .package(
        url: "https://github.com/example/optional-lib.git",
        from: "1.0.0"
    )
],
targets: [
    .target(
        name: "MyPlugin",
        dependencies: [
            .product(name: "OptionalLib", package: "optional-lib",
                     condition: .when(traits: ["OptionalFeature"]))
        ],
        swiftSettings: [
            .define("INCLUDE_OPTIONAL_FEATURE",
                    .when(traits: ["OptionalFeature"]))
        ])
]
```

**4. Guard your code with conditional compilation:**

```swift
#if INCLUDE_OPTIONAL_FEATURE
import OptionalLib
#endif
```

Keep in mind that traits should be additive — enabling a trait should add functionality, not remove it. This aligns with semantic versioning expectations and avoids surprises for consumers.

## Requirements and Limitations

This feature is currently marked as **experimental** because Capacitor does not yet officially support Swift 6. Here's what you need:

| Requirement   | Minimum Version |
| ------------- | --------------- |
| Capacitor CLI | 8.3.0+          |
| Xcode         | 16.3+           |
| Swift         | 6.1+            |

A few things to keep in mind:

- **Compatibility**: Setting `swift-tools-version` to `6.1` can cause issues with dependencies that were built with older Swift versions, particularly those using XCFrameworks. Test your app thoroughly after enabling this setting.
- **Experimental namespace**: Both config options live under the `experimental` block, signaling that the API may change in future Capacitor versions.
- **Swift 6 timeline**: The Capacitor team has indicated that full Swift 6 support is planned for a future major version (likely Capacitor 9), at which point these settings may move out of the experimental namespace.

## FAQ

### Why is this feature marked experimental if it already works in Capacitor 8.3.0+?

Because it depends on Swift 6, which Capacitor doesn't officially support yet. The feature works today, but both config options live under the `experimental` namespace specifically to signal that the API may change once Capacitor adds full Swift 6 support — likely in Capacitor 9. Treat it as usable but not yet a stable, permanent API surface.

### Will setting `swiftToolsVersion` to `6.1` break other CocoaPods or SPM dependencies in my project?

It can, particularly with dependencies distributed as XCFrameworks built against older Swift versions. This is the main compatibility risk called out in the guide — test your app thoroughly after enabling this setting rather than assuming every existing dependency tolerates the newer tools version.

### Do I need to enable a trait to use the SQLite plugin at all, or only for encryption?

Only for encryption. The plugin ships a `SQLite` trait as the default, enabled automatically — the `SQLCipher` trait is what you opt into specifically to add encrypted database support. Skipping the trait config entirely still gives you a working, unencrypted SQLite plugin.

### As a plugin author, can enabling a trait remove functionality that's present by default?

That would go against how traits are meant to be designed. The guide's own guidance is that traits should be additive — enabling one should add functionality, not take it away — since that matches semantic versioning expectations and avoids surprising consumers who enable a trait expecting only new capabilities, not different default behavior.

### Is there a CocoaPods equivalent if I don't want to touch the experimental SPM namespace yet?

Yes — subspecs served this same purpose before SPM traits existed, and are what this feature is explicitly filling the gap for. See [How to Use CocoaPods Instead of SPM with Capacitor](./how-to-use-cocoapods-with-capacitor.md) if you'd rather stay off the experimental trait config for now.

## Related Posts

- [How to Migrate a Capacitor App to Swift Package Manager](./how-to-migrate-a-capacitor-app-to-spm.md)
- [How to Migrate a Capacitor Plugin to SPM](./how-to-migrate-a-capacitor-plugin-to-spm.md)
- [How to Use CocoaPods Instead of SPM with Capacitor](./how-to-use-cocoapods-with-capacitor.md)

## Try Capawesome Cloud

Build, deploy, and update your Capacitor apps with ease using Capawesome Cloud — with native builds, live updates, and app store publishing all in one platform.

[Try Capawesome Cloud Free](/){ .md-button .md-button--primary }

## Conclusion

SPM package traits bring much-needed parity with CocoaPods for managing optional dependencies in Capacitor plugins. For app developers, it means you can now opt in to features like SQLCipher encryption with a simple config change. For plugin authors, it opens the door to offering flexible, modular plugins without maintaining separate packages.

If you want to learn more about encrypting SQLite databases with the SQLCipher trait, check out the guide on [Encrypting SQLite databases in Capacitor](./encrypting-capacitor-sqlite-database.md).

If you have questions or want to share how you're using traits in your plugins, join the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}. And subscribe to the [Capawesome newsletter](/newsletter/){:target="_blank"} to stay up to date on the latest Capacitor news and plugin releases.
