Para usar package traits de SPM en Capacitor 8, establece experimental.ios.spm.swiftToolsVersion a "6.1" y enumera los traits por plugin en experimental.ios.spm.packageTraits en tu configuración de Capacitor, luego ejecuta bunx cap sync ios. El CLI escribe esos traits en el CapApp-SPM/Package.swiftgenerado, y SwiftPM compila el plugin con las dependencias opcionales y rutas code correspondientes. Esto requiere Capacitor CLI 8.3.0 o superior y solo aplica a proyectos iOS que usen Swift Package Manager.
Los traits cierran la última gran brecha de funciones entre SPM y CocoaPods para plugins: dependencias nativas opcionales que antes eran subspecs de podspec. Esta guía cubre el lado de la app, el lado del autor del plugin y los límites del soporte experimental actual.
Qué son los package traits
Los package traits provienen de la propuesta de Swift Evolution SE-0450, implementada en Swift 6.1 (Xcode 16.3 y posteriores). Un paquete declara traits con nombre en su Package.swift. Los consumidores eligen cuáles habilitar al depender del paquete. Un trait puede:
- hacer que una dependencia sea opcional (
condition: .when(traits: [...])en una dependencia de target), - activa las rutas de code, ya que cada trait habilitado está disponible como una condición de compilación (
#if TraitName), - cambia ajustes de build como defines o linker flags,
- habilita otros traits.
Un paquete también puede marcar traits como predeterminados. Se activan a menos que el consumidor pase una lista explícita.
Si conoces los subspecs de CocoaPods o las features de Cargo en Rust, es la misma idea.
Por qué importa en Capacitor
Capacitor genera el archivo de la app Package.swift por ti, así que no puedes editar manualmente las líneas de dependencias para pasar traits. Antes de CLI 8.3, los autores de plugins con un SDK nativo opcional tenían dos malas opciones en SPM: enlazar siempre el SDK, aumentando el tamaño y a veces añadiendo trabajo de privacidad o cumplimiento de exportación, o publicar un segundo plugin. Casos comunes:
- SQLite cifrado vs sin cifrar (SQLCipher),
- SDKs de analytics con y sin recogida del identificador publicitario,
- un SDK de proveedor opcional para un proveedor de pagos o login,
- herramientas solo para debug.
Habilita los traits en tu app
1. Verifica las versiones
bunx cap --version # 8.3.0 or later
xcodebuild -version # Xcode 26 (required by Capacitor 8 anyway)
El proyecto debe usar SPM: ios/App/CapApp-SPM existe. Si usas CocoaPods, mira cómo migrar tu app Capacitor a SPM.
2. Busca los nombres de los traits
Abre el plugin Package.swift en node_modules y busca el/la traits: array, o revisa su README. Los nombres de los traits distinguen mayúsculas.
3. Configura 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;
La clave es el nombre del paquete npm del plugin. El valor es la lista de traits a habilitar.
4. Sincroniza y revisa el output
bunx cap sync ios
El generado ios/App/CapApp-SPM/Package.swift ahora empieza con // swift-tools-version: 6.1 y la línea de dependencia del plugin incluye los traits:
.package(name: "AcmeCapacitorDb", path: "../../../node_modules/@acme/capacitor-db", traits: [.defaults, "SQLCipher"])
El CLI escribe .defaults (o defaults, .default, default en cualquier caso) como el SwiftPM .defaults valor, y el resto de nombres como strings entre comillas.
Build en Xcode. Si Xcode sigue mostrando el grafo antiguo, usa Archivo > Paquetes > Restablecer caché de paquetes.
Reglas que CLI aplica
El Capacitor CLI valida la configuración durante la sincronización:
- Si algún plugin tiene una lista de traits no vacía y
swiftToolsVersionfalta, la sincronización se detiene con: “Package traits require an explicit Swift tools version of 6.1 or higher.” - Si
swiftToolsVersiones inferior a 6.1, la sincronización se detiene y te avisa que la versión es demasiado baja. swiftToolsVersiondebe verse así6.1o6.1.0.- Los plugins con un array vacío se tratan como si no estuvieran listados.
Mantener o descartar traits por defecto
Listar traits reemplaza los traits por defecto. Compara:
| Valor de configuración | Resultado |
|---|---|
| no listado | traits por defecto del plugin |
['SQLCipher'] |
solo SQLCipher, por defecto desactivado |
['.defaults', 'SQLCipher'] |
por defecto y adicionales SQLCipher |
Lee la documentación del plugin antes de quitar los defaults. Un plugin podría tener su backend estándar detrás de un trait por defecto.
Añade traits a tu propio plugin
Si mantienes un plugin con un SDK opcional, aquí tienes uno completo Package.swift. El nombre del paquete debe coincidir con el nombre del cual deriva el Capacitor CLI de tu npm ("@acme/capacitor-db pasa a ser AcmeCapacitorDb). Consulta migrar un plugin de Capacitor a SPM para la regla.
// 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]
)
Consulta el repositorio propio de SDK para verificar su URL de paquete, nombre del producto y versión antes de copiar esto.
Detalles que es fácil pasar por alto:
- La versión 6.x de Tools cambia el modo de lenguaje predeterminado a Swift 6 para tus targets. Capacitor aún no soporta Swift 6 oficialmente, así que mantén
swiftLanguageModes: [.v5]a menos que tu code esté listo para strict concurrency. - Subir la versión de las herramientas de tu plugin a 6.1 implica que los consumidores necesiten Xcode 16.3+. Las apps de Capacitor 8 ya requieren Xcode 26, así que no es una restricción nueva.
Usa el trait en code
Los traits habilitados son condiciones de compilación en los targets de tu package:
#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
}
Devuelve un error claro que nombre el trait cuando una función esté desactivada. De lo contrario, los desarrolladores de apps verán un fallo silencioso y culparán al plugin.
También puedes definir tu propio flag con swiftSettings: [.define("ACME_SQLCIPHER", .when(traits: ["SQLCipher"]))] si prefieres un nombre con prefijo.
Diseña traits que sean aditivos
SwiftPM unifica los traits en todo el grafo de dependencias. Si dos paquetes dependen de tu plugin con diferentes traits, se habilita la unión. Así:
- Habilitar un trait debe añadir capacidades, no eliminar API ni cambiar los valores por defecto.
- Evita traits mutuamente excluyentes. Si no puedes, haz que el build falle con un error claro:
#if TraitA && TraitB
#error("TraitA and TraitB cannot be enabled together")
#endif
Prueba cada combinación
swift build por sí solo compila para Mac, y un plugin que depende de Capacitor solo compila para iOS, así que prueba los traits con una app de prueba Capacitor compilada para un simulador de iOS. Para cada combinación (sin entrada para los traits por defecto, ['SQLCipher'], ['.defaults', 'SQLCipher'] y así sucesivamente), cambia packageTraits en la configuración de la app de prueba, luego sincroniza y compila:
bunx cap sync ios
xcodebuild build \
-project ios/App/App.xcodeproj \
-scheme App \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO
Añade cada combinación al CI como un job independiente.
Mantén cubiertos a los usuarios de CocoaPods
Los Traits solo existen en SPM. Si tu plugin también incluye un podspec, ofrece la misma opción mediante 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
Usar el mismo nombre de condición de compilación en el subspec te permite compartir uno #if SQLCipher La ruta code. Las apps con CocoaPods seleccionan luego el subspec en su Podfile. El Capacitor CLI escribe una pod una línea por plugin dentro de capacitor_pods, así que documenta la línea extra del Podfile en tu README. Cómo usar CocoaPods con Capacitor 8 muestra dónde van las líneas de pods personalizadas.
Limitaciones
- Experimental. Las opciones se encuentran en
experimentaly podrían moverse aios.spm.*in a future major. Expect a config rename at some point. - Swift 6 no tiene soporte oficial. La propia documentación de CLI advierte que configurar
swiftToolsVersiona la 6.0 o superior podría causar problemas. Prueba la app completa, sobre todo los plugins que incluyen binarios xcframework. - Solo iOS. Android no tiene equivalente en Capacitor. Los plugins gestionan los SDK de Android opcionales con variables de Gradle en
variables.gradleo artefactos separados. - Los plugins deben activarlo explícitamente. La mayoría de los plugins aún no definen traits. Verifícalo antes de planear uno.
Solución de problemas
El sync falla con “Package traits require an explicit Swift tools version of 6.1 or higher”. Añadir swiftToolsVersion: '6.1' junto a packageTraits.
SwiftPM reporta un trait desconocido. Los nombres de los traits distinguen mayúsculas y deben existir en el Package.swift para la versión instalada.
La funcionalidad sigue sin aparecer tras activar el trait. Reinicia el caché de paquetes en Xcode, limpia la carpeta de build y confirma que el CapApp-SPM/Package.swift tiene el traits: sufijo. Editar ese archivo a mano no sirve, ya que CLI lo sobrescribe en cada sync.
Errores en Swift code tras subir la versión de las herramientas. Es el modo de lenguaje Swift 6 en un paquete que controlas. Añade swiftLanguageModes: [.v5].
Conflictos de nombres entre plugins. Desde CLI 8.4, experimental.ios.spm.packageOptions te permite configurar symlink o moduleAliases por plugin. Más detalles en el Guía de troubleshooting de iOS de Capacitor.
Para entender mejor el panorama de SPM, lee SPM vs CocoaPods para Capacitor. Si buscas cloud builds que ya ejecuten la versión actual de Xcode para proyectos SPM, consulta Build de Capgo.