跳过主要内容
Capacitor iOS 配置

如何在 Capacitor 中使用 SPM 包特性 8

使用 SPM 包 trait 在 Capacitor 8 中:启用可选插件功能,设置 swiftToolsVersion 6.1,并将 trait 添加到您的插件中。

文章来源

马丁·多纳迪厄

作者

瓦莱里娅

审阅者

乔丹

编辑

如何在 Capacitor 8 中使用 SPM 包 trait

要在 Capacitor 8 中使用 SPM 包 trait,设置 experimental.ios.spm.swiftToolsVersion to "6.1" 并 experimental.ios.spm.packageTraits 在您的 Capacitor 配置中,然后运行 bunx cap sync ios. CLI 将这些特性写入生成的 CapApp-SPM/Package.swift,并且SwiftPM使用匹配的可选依赖项和 code 路径构建插件。这需要 Capacitor CLI 8.3.0或更高版本,只适用于使用Swift Package Manager的iOS项目。

特性填补了SPM和CocoaPods之间的最后一个大功能差距:可选的本机依赖项,之前是podspec子规范。这份指南涵盖了应用程序侧、插件作者侧和当前实验性支持的限制。

什么是包特性

包特性来自Swift Evolution提案 SE-0450,在Swift 6.1(Xcode 16.3和更高版本)中实现。包声明其 Package.swift中的命名特性。消费者在依赖包时选择启用哪些。一个特性可以:

  • 使一个依赖项可选(condition: .when(traits: [...]) 在一个目标依赖项上),
  • 启用 code 路径,因为每个启用的特性都可作为编译条件 (#if TraitName),
  • toggle 构建设置,如定义或链接器标志
  • 启用其他特性。

一个包也可以将特性标记为默认值。它们是开启的,除非消费者传递了明确的列表。

如果您熟悉 CocoaPods 子规范或 Cargo 特性在 Rust 中,那么这是相同的概念。

为什么在 Capacitor 中它很重要

Capacitor 为您生成应用的 Package.swift 依赖项列表,因此您无法手动编辑依赖项行来传递特性。 在 CLI 8.3 之前,插件作者有一个可选的本机 SDK,在 SPM 上有两个坏选择:始终链接 SDK,增加大小并有时添加隐私或出口合规工作,或者发布第二个插件。 常见场景:

  • 加密 vs 平台 SQLite (SQLCipher)
  • 分析 SDKs 有和没有广告标识符收集
  • 一个可选的供应商 SDK,用于一个付款或登录提供者
  • 调试工具。

在您的应用中启用特性

1. 检查版本

bunx cap --version      # 8.3.0 or later
xcodebuild -version     # Xcode 26 (required by Capacitor 8 anyway)

该项目必须使用SPM: ios/App/CapApp-SPM 存在。如果您使用CocoaPods,请参阅 如何将您的Capacitor应用迁移到SPM.

2. 查找插件的特性名称

打开插件的 Package.swift 在 node_modules 并查找 traits: 数组,或者检查其README。特性名称敏感。

3. 配置Capacitor

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  experimental: {
    ios: {
      spm: {
        swiftToolsVersion: '6.1',
        packageTraits: {
          '@acme/capacitor-db': ['.defaults', 'SQLCipher'],
        },
      },
    },
  },
};

export default config;

关键是插件的npm包名。值是要启用的特性列表。

4. 同步并检查输出

bunx cap sync ios

The generated ios/App/CapApp-SPM/Package.swift 现在开始 // swift-tools-version: 6.1 并且插件的依赖行包含了这些特性:

.package(name: "AcmeCapacitorDb", path: "../../../node_modules/@acme/capacitor-db", traits: [.defaults, "SQLCipher"])

The CLI writes .defaults (or defaults, .default, default context:HTML text fragment from a longer Capgo UI string (parent key `compare_step_build_text`). Page/area: Capgo marketing website. Role: Long marketing or legal paragraph. Seen in: page alternatives/expo.astro, page alternatives/voltbuilder.astro. Message key `compare_step_build_text` (Compare Step Build Text). .defaults in any case) as the SwiftPM

value, and every other name as a quoted string. Build in Xcode. If Xcode still shows the old graph, use.

Rules the CLI enforces

Capacitor CLI 在同步过程中验证配置。

  • 如果任何插件具有非空的特性列表并且 swiftToolsVersion 缺少了,同步停止并显示“包特性需要显式的Swift工具版本6.1或更高。”
  • 如果 swiftToolsVersion 版本低于6.1,同步停止并提示版本太低。
  • swiftToolsVersion 必须像 6.1 或 6.1.0.
  • 或

Keep or drop default traits

Listing traits replaces the defaults. Compare:

Config value Result
not listed plugin’s default traits
['SQLCipher'] 仅此 SQLCipher, 默认关闭
['.defaults', 'SQLCipher'] 默认加 SQLCipher

阅读插件文档之前不要关闭默认值。一个插件可能会将其标准后端放在一个默认特性后面。

为您的插件添加特性

如果您维护一个可选的SDK插件,以下是一个完整的 Package.swift. 包名必须与 Capacitor CLI 从您的 npm 名称派生的名称匹配。@acme/capacitor-db 变成 AcmeCapacitorDb请参见 将Capacitor插件迁移到SPM 了解规则。

// swift-tools-version: 6.1
import PackageDescription

let package = Package(
    name: "AcmeCapacitorDb",
    platforms: [.iOS(.v15)],
    products: [
        .library(name: "AcmeCapacitorDb", targets: ["DbPlugin"])
    ],
    traits: [
        .trait(name: "SQLCipher", description: "Link SQLCipher and enable encrypted databases."),
        .default(enabledTraits: [])
    ],
    dependencies: [
        .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
        .package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.10.0")
    ],
    targets: [
        .target(
            name: "DbPlugin",
            dependencies: [
                .product(name: "Capacitor", package: "capacitor-swift-pm"),
                .product(name: "Cordova", package: "capacitor-swift-pm"),
                .product(
                    name: "SQLCipher",
                    package: "SQLCipher.swift",
                    condition: .when(traits: ["SQLCipher"])
                )
            ],
            path: "ios/Sources/DbPlugin"
        )
    ],
    swiftLanguageModes: [.v5]
)

在复制此内容之前,请检查SDK的当前包URL、产品名称和版本。

容易忽略的点:

  • 工具版本 6.x 将默认语言模式更改为 Swift 6 对于您的目标。Capacitor 尚未正式支持 Swift 6,因此请勿 swiftLanguageModes: [.v5] 除非您的code 已准备好严格的并发性。
  • 将您的插件工具版本升级到 6.1 意味着消费者需要 Xcode 16.3+。 Capacitor 已经有 8 个应用程序要求 Xcode 26,因此这并不是一个新的约束。

在code 中使用该特性。

启用的特性是您的包目标的编译条件:

#if SQLCipher
import SQLCipher
#endif

@objc func open(_ call: CAPPluginCall) {
    let key = call.getString("encryptionKey")
    #if SQLCipher
    // open with key
    #else
    if key != nil {
        call.unavailable("Encryption requires the SQLCipher trait. Enable it in experimental.ios.spm.packageTraits.")
        return
    }
    #endif
    // open without encryption
}

返回一个明确的错误,指明特性何时被关闭。应用开发者否则会看到一个静默的失败,并将其归咎于插件。

您还可以使用 swiftSettings: [.define("ACME_SQLCIPHER", .when(traits: ["SQLCipher"]))] 如果您更喜欢带前缀的名称。

设计特性以可加性

SwiftPM 将特性统一到依赖图中。如果两个包依赖于您的插件具有不同的特性,会启用联合。因此:

  • 启用特性应该添加功能,而不是移除 API 或改变默认值。
  • 避免互斥特性。如果无法避免,请在构建时大声失败:
#if TraitA && TraitB
#error("TraitA and TraitB cannot be enabled together")
#endif

测试每种 combination

swift build 在 Mac 上单独构建,并且一个插件依赖于 Capacitor,仅在 iOS 上构建,因此测试特性通过一个 Capacitor 测试应用程序,构建为 iOS 模拟器。对于每种 combination(无默认特性入口, ['SQLCipher'], ['.defaults', 'SQLCipher'] 然后在测试应用程序的配置中改变 packageTraits 然后同步并构建:

bunx cap sync ios
xcodebuild build \
  -project ios/App/App.xcodeproj \
  -scheme App \
  -destination 'generic/platform=iOS Simulator' \
  CODE_SIGNING_ALLOWED=NO

将每种 combination 添加到 CI 作为单独的工作项。

保持 CocoaPods 用户受益

特性仅在 SPM 中存在。如果您的插件也提供 podspec,提供相同的选择,如 subspecs:

s.default_subspec = 'Core'

s.subspec 'Core' do |core|
  core.source_files = 'ios/Sources/DbPlugin/**/*.swift'
  core.dependency 'Capacitor'
end

s.subspec 'SQLCipher' do |sc|
  sc.dependency 'AcmeCapacitorDb/Core'
  sc.dependency 'SQLCipher', '~> 4.10'
  sc.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => '$(inherited) SQLCipher' }
end

将相同的编译条件名称设置在 subspec 中,让您共享一个 #if SQLCipher code 路径。CocoaPods 应用程序然后在 Podfile 中选择 subspec。 Capacitor CLI 写入一个 pod 每个插件内部的行数 capacitor_pods,因此在 README 中记录额外的 Podfile 行。 如何使用 CocoaPods 与 Capacitor 8 显示自定义 Pod 行的位置。

限制

  • 实验性。 选项位于 experimental 并可能在未来重大版本中移动。请预期某个时候会更改配置。 ios.spm.* Swift 6 未获得官方支持。
  • __CAPGO_KEEP_0__ 的文档警告,设置 The CLI的官方文档警告称,设置 swiftToolsVersion Experimental.
  • iOS 只有。 Android 没有对应项。Capacitor 插件使用 Gradle 变量在 variables.gradle 或分离的 artifact。
  • 插件必须自愿参与。 大多数插件尚未定义特性。请在计划使用特性之前检查。

故障排除

页面/区域: 支持 / 高级支持页面或底部支持部分。角色: 小节或页面标题。见于: 页面 support-policy.astro。消息键 `support_policy_troubleshooting_title` (支持政策故障排除标题)。 Add swiftToolsVersion: '6.1' 添加 packageTraits.

SwiftPM 报告未知特性。 下一个 Package.swift SwiftPM 报告未知特性。

功能仍然缺失,尽管已启用特性。 在 Xcode 中重置包缓存,清除构建文件夹,并确认生成的 CapApp-SPM/Package.swift 有 traits: 后缀。手动编辑该文件无效,因为每次同步时 CLI 都会重写它。

Swift code 中的错误,升级工具版本后。 Swift 6 语言模式在您控制的包中。添加 swiftLanguageModes: [.v5].

插件之间的名称冲突。 自 CLI 8.4 开始 experimental.ios.spm.packageOptions 允许您设置 symlink 或 moduleAliases 由于 __CAPGO_KEEP_0__ 8.4 开始允许您设置或选择,因此您可以选择使用 Capgo 或其他替代方案。 每个插件都有一个特定的设置选项。更多信息请参见 Capacitor iOS 故障排除指南。.

为了更大的SPM图景,请阅读 SPM与CocoaPods的Capacitor. 如果您想在云端构建当前Xcode的SPM项目,请参阅 Capgo 构建.

Capacitor 应用的即时更新

当web层bug出现时,通过 Capgo 发布修复,而不是等待几天的应用商店审批。用户在后台接收更新,而原生变化仍在正常审批路径中。

最新博客文章

Capgo 为您提供了创建真正专业的移动应用所需的最佳见解。