Salta al contenuto principale

Come usare i Package Traits di SPM in Capacitor 8

Usa i package traits SPM in Capacitor 8: abilita funzionalità opzionali dei plugin con packageTraits, imposta swiftToolsVersion 6.1 e aggiungi traits al tuo plugin.

Illustrazione per la configurazione di Swift Package Manager nei progetti iOS di Capacitor

Per usare i package traits di SPM in Capacitor 8, imposta experimental.ios.spm.swiftToolsVersion a "6.1" e elenca i traits per plugin sotto experimental.ios.spm.packageTraits nella configurazione di Capacitor, quindi esegui bunx cap sync ios. CLI scrive quei traits nel file generato CapApp-SPM/Package.swift, e SwiftPM compila il plugin con le dipendenze opzionali corrispondenti e i percorsi code. Richiede Capacitor CLI 8.3.0 o successivo e vale solo per i progetti iOS che usano Swift Package Manager.

I traits colmano l'ultimo grande divario funzionale tra SPM e CocoaPods per i plugin: le dipendenze native opzionali che prima erano subspec dei podspec. Questa guida copre il lato app, il lato autore del plugin e i limiti del supporto sperimentale attuale.

Cosa sono i package traits

I package traits derivano dalla proposta Swift Evolution SE-0450, implementata in Swift 6.1 (Xcode 16.3 e successivi). Un package dichiara trait nominati nel suo Package.swift. I consumer scelgono quali abilitare quando dipendono dal pacchetto. Un trait può:

  • rendere opzionale una dipendenza (condition: .when(traits: [...]) su una dipendenza target),
  • attiva i percorsi code, dato che ogni trait abilitato è disponibile come condizione di compilazione (#if TraitName),
  • attiva/disattiva impostazioni di build come defines o linker flags,
  • abilita altri trait.

Un pacchetto può anche impostare dei trait come predefiniti. Saranno attivi a meno che l'utente non passi un elenco esplicito.

Se conosci i subspecs di CocoaPods o le feature di Cargo in Rust, il concetto è lo stesso.

Perché è importante in Capacitor

Capacitor genera l'app Package.swift per te, quindi non puoi modificare a mano le righe delle dipendenze per passare i trait. Prima di CLI 8.3, gli autori di plugin con un SDK nativo opzionale avevano due brutte scelte su SPM: linkare sempre il SDK, aumentando le dimensioni e a volte aggiungendo lavoro su privacy o export compliance, oppure pubblicare un secondo plugin. Casi comuni:

  • SQLite cifrato vs in chiaro (SQLCipher),
  • SDK analytics con o senza raccolta dell'advertising identifier,
  • un SDK opzionale del vendor per un provider di pagamenti o login,
  • strumenti solo per il debug.

Abilita i trait nella tua app

1. Controlla le versioni

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

Il progetto deve usare SPM: ios/App/CapApp-SPM esiste. Se usi CocoaPods, vedi come migrare la tua app Capacitor a SPM.

2. Trova i nomi dei trait del plugin

Apri il plugin Package.swift nella node_modules e cerca l'array traits: o controlla il README. I nomi dei trait sono case-sensitive.

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 chiave è il nome del package npm del plugin. Il valore è la lista dei trait da abilitare.

4. Sincronizza e controlla l'output

bunx cap sync ios

Il file generato ios/App/CapApp-SPM/Package.swift ora inizia con // swift-tools-version: 6.1 e la riga delle dipendenze del plugin riporta i trait:

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

Il CLI scrive .defaults (o defaults, .default, default in ogni caso) come il SwiftPM .defaults valore, e ogni altro nome come una stringa tra virgolette.

Build in Xcode. Se Xcode mostra ancora il vecchio grafo, usa File > Package > Reset Package Caches.

Le regole che CLI impone

Il Capacitor CLI convalida la config durante la sync:

  • Se un plugin ha un elenco di trait non vuoto e swiftToolsVersion manca, la sincronizzazione si arresta con “Package traits require an explicit Swift tools version of 6.1 or higher.”
  • Se swiftToolsVersion è inferiore a 6.1, la sincronizzazione si arresta e segnala che la versione è troppo bassa.
  • swiftToolsVersion deve essere nel formato 6.1 oppure 6.1.0.
  • I plugin con un array vuoto vengono trattati come se non fossero elencati.

Mantenere o rimuovere i trait di default

Elencare i trait sostituisce i valori predefiniti. Confronta:

Valore di configurazione Risultato
non elencato trait predefiniti del plugin
['SQLCipher'] solo SQLCipher, disattivato di default
['.defaults', 'SQLCipher'] default più SQLCipher

Leggi la documentazione del plugin prima di rimuovere i default. Un plugin potrebbe nascondere il backend standard dietro un trait di default.

Aggiungere i trait al proprio plugin

Se mantieni un plugin con un SDK opzionale, ecco un esempio completo Package.swift. Il nome del pacchetto deve corrispondere al nome da cui Capacitor CLI deriva il tuo nome npm (@acme/capacitor-db diventa AcmeCapacitorDb). Vedi migrare un plugin Capacitor a SPM per la regola.

// 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]
)

Controlla il repository di SDK per l'URL del pacchetto, il nome del prodotto e la versione attuali prima di copiare questo.

Punti facili da trascurare:

  • Tools version 6.x cambia la modalità di linguaggio predefinita in Swift 6 per i tuoi target. Capacitor non supporta ufficialmente Swift 6, quindi mantieni swiftLanguageModes: [.v5] a meno che il tuo code non sia pronto per la concorrenza rigorosa.
  • Portare la versione degli strumenti del plugin a 6.1 richiede Xcode 16.3+ per gli utenti. Le app Capacitor 8 richiedono già Xcode 26, quindi non è un vincolo nuovo.

Usare il trait in code

I trait abilitati sono condizioni di compilazione nei target del tuo pacchetto:

#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
}

Restituisci un errore chiaro che specifichi il trait quando una feature è disattivata. Altrimenti gli sviluppatori dell'app vedranno un fallimento silenzioso e colpevolizzeranno il plugin.

Puoi anche definire il tuo flag con swiftSettings: [.define("ACME_SQLCIPHER", .when(traits: ["SQLCipher"]))] se preferisci un nome con prefisso.

Progettare i trait in modo additivo

SwiftPM unifica i trait in tutto il grafo delle dipendenze. Se due pacchetti dipendono dal tuo plugin con trait diversi, viene abilitata l'unione. Quindi:

  • Abilitare un trait dovrebbe aggiungere capacità, non rimuovere API o modificare i valori predefiniti.
  • Evita trait mutualmente esclusivi. Se non puoi, fallisci la build in modo esplicito:
#if TraitA && TraitB
#error("TraitA and TraitB cannot be enabled together")
#endif

Testare ogni combinazione

swift build da solo compila per Mac, e un plugin che dipende da Capacitor compila solo per iOS, quindi testa i trait tramite un'app di test Capacitor compilata per un iOS Simulator. Per ogni combinazione (nessuna voce per i trait predefiniti, ['SQLCipher'], ['.defaults', 'SQLCipher'] e così via), modifica packageTraits nella configurazione dell'app di test, quindi sincronizza e compila:

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

Aggiungi ogni combinazione a CI come un job separato.

Copertura per gli utenti di CocoaPods

I traits esistono solo in SPM. Se il tuo plugin include anche un podspec, offri la stessa scelta tramite subspec:

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

Impostando lo stesso nome di condizione di compilazione nella subspec, puoi condividere un unico #if SQLCipher percorso code. Le app CocoaPods selezionano quindi la subspec nel loro Podfile. Il Capacitor CLI scrive una pod riga per plugin all'interno di capacitor_pods, quindi documenta la riga aggiuntiva del Podfile nel tuo README. Come usare CocoaPods con Capacitor 8 mostra dove inserire le righe pod personalizzate.

Limitazioni

  • Sperimentale. Le opzioni si trovano sotto experimental e potrebbero spostarsi in ios.spm.* in una futura major. Aspettati un rename della config in qualche momento.
  • Swift 6 non supportato ufficialmente. La documentazione di CLI avverte che impostare swiftToolsVersion passare alla 6.0 o a versioni superiori potrebbe causare problemi. Testa l'app completa, soprattutto i plugin che includono xcframework binari.
  • Solo iOS. Android non ha un equivalente in Capacitor. I plugin gestiscono gli SDK Android opzionali tramite variabili Gradle in variables.gradle o tramite artifact separati.
  • I plugin devono aderire esplicitamente. La maggior parte dei plugin non definisce ancora i trait. Controlla prima di farci affidamento.

Risoluzione problemi

La sincronizzazione fallisce con: “Package traits require an explicit Swift tools version of 6.1 or higher”. Aggiungi swiftToolsVersion: '6.1' accanto a packageTraits.

SwiftPM segnala un trait sconosciuto. I nomi dei trait sono case-sensitive e devono esistere nel Package.swift per la versione installata.

Funzionalità ancora mancante dopo aver abilitato il trait. Resetta le cache dei pacchetti in Xcode, pulisci la cartella di build e verifica il file generato CapApp-SPM/Package.swift ha traits: . La modifica manuale di quel file non viene mantenuta, poiché CLI lo riscrive a ogni sync.

Errori in Swift code dopo l'aggiornamento della versione degli strumenti. Si tratta della modalità linguaggio Swift 6 in un pacchetto che controlli. Aggiungi swiftLanguageModes: [.v5].

Conflitti di nome tra plugin. Da CLI 8.4, experimental.ios.spm.packageOptions ti permette di impostare symlink oppure moduleAliases per plugin. Ne parliamo più nel Guida alla risoluzione problemi iOS di Capacitor.

Per una visione più ampia di SPM, leggi SPM vs CocoaPods per Capacitor. Se desideri build cloud che eseguono già l'ultima versione di Xcode per i progetti SPM, consulta Build Capgo.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.