> For the complete documentation index, see [llms.txt](https://cmp.docs.sirdata.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cmp.docs.sirdata.net/applications-mobiles-sdk-natif/mobile-sdk/setup-1.md).

# iOS — Installation

Cette page vous mène d'un projet Xcode vide à une intégration prête pour l'App Store, dans l'ordre. Les étapes 1 à 6 sont l'intégration elle-même ; les étapes 7 à 10 couvrent les utilisateurs américains, les tests, la release et les montées de version.

Chaque extrait Swift ci-dessous compile tel quel contre le paquet 2.0.0 publié, sauf ceux marqués *fragment*, qui se placent dans votre propre code.

## 1. Prérequis

|                       | Exigence                                                                                                                                      |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Deployment target iOS | 15.0 ou plus, quel que soit le canal                                                                                                          |
| Xcode                 | Vérifié avec Xcode 27.0 ; les versions antérieures n'ont pas été testées. Le paquet déclare les Swift tools 5.9, qui exigent Xcode 15 ou plus |
| Swift                 | 5.9 ou plus                                                                                                                                   |
| Sirdata               | Un **partner ID** et un **config ID**, disponibles dans la [Console Sirdata](https://cmp.sirdata.io)                                          |

Le binaire 2.0.0 lui-même est construit avec Xcode 26, la version figée dans la recette de build du SDK. C'est un framework statique avec une interface Objective-C ; l'interface de consentement est livrée sous forme de sources Swift que votre Xcode compile.

Le partner ID et le config ID désignent la configuration éditeur que le SDK télécharge au lancement : textes, thème, finalités, partenaires, périmètre et capping. Une modification de configuration dans la Console Sirdata prend effet sans nouvelle release de l'application.

**Licence.** Le SDK est un logiciel propriétaire, concédé sous licence et non vendu, sous la [Sirdata CMP Mobile SDK Proprietary Licence v1.1](https://sirdata.com/legal/sdk-licence-v1.1.txt), qui est aussi le fichier `LICENSE` des paquets Swift Package Manager et CocoaPods. Livrer le SDK dans une application mise à la disposition d'utilisateurs exige un abonnement valide et en cours à la Sirdata Consent Management Platform ; un abonnement pris par la Console Sirdata suffit, et aucun accord signé n'est requis (section 25.1). Sans abonnement, la licence est une licence d'évaluation de 90 jours : évaluation interne, développement, tests et intégration, sans distribution à des tiers ni sur un store d'applications (section 1.4). Abonnements : [sirdata.com](https://sirdata.com) ; questions sur la licence : <legal@sirdata.com>.

## 2. Ajouter le SDK au projet

Utilisez Swift Package Manager ou CocoaPods : les deux livrent le framework du cœur **et** l'interface de consentement SwiftUI. Le XCFramework manuel n'a pas d'interface.

### Swift Package Manager dans Xcode (recommandé)

1. **File → Add Package Dependencies…**, puis saisissez `https://github.com/SirDataFR/sirdata-cmp-ios-spm`.
2. Réglez la règle de dépendance sur **Up to Next Major Version**, à partir de **2.0.0**.
3. Ajoutez le produit **SirDataCMP** à la cible de votre application.

### Swift Package Manager dans un `Package.swift`

Déclarez le paquet, puis le produit dans chaque cible qui l'utilise. Le libellé `package:` est le dernier composant de l'URL :

```swift
// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "MyFeature",
    platforms: [.iOS(.v15)],
    dependencies: [
        .package(url: "https://github.com/SirDataFR/sirdata-cmp-ios-spm", from: "2.0.0")
    ],
    targets: [
        .target(
            name: "MyFeature",
            dependencies: [
                .product(name: "SirDataCMP", package: "sirdata-cmp-ios-spm")
            ]
        )
    ]
)
```

### CocoaPods

Dans votre `Podfile` :

```ruby
platform :ios, '15.0'

target 'MyApp' do
  pod 'SirdataCMP', '~> 2.0'
end
```

Lancez `pod install`, puis ouvrez le `.xcworkspace`, pas le `.xcodeproj`. Si CocoaPods ne trouve pas la version 2.0, lancez `pod install --repo-update`. Le pod fonctionne avec ou sans `use_frameworks!`.

Le pod s'appelle `SirdataCMP`, avec un *d* minuscule. Gardez la contrainte `~> 2.0` : les versions 1.x de ce pod sont un autre SDK Sirdata, abandonné, de 2020, construit à partir d'une autre base de code.

### Manuel (XCFramework)

Téléchargez `SirDataCMP-2.0.0.xcframework.zip` depuis la [page des releases](https://github.com/SirDataFR/sirdata-cmp-ios-spm/releases), décompressez-le, glissez `SirDataCMP.xcframework` dans votre projet, et dans **General → Frameworks, Libraries, and Embedded Content** de votre cible, réglez-le sur **Do Not Embed** : c'est un framework statique, lié dans le binaire de votre application.

Cette archive ne contient que le module `SirDataCMP` du cœur, et aucun des fichiers de licence de l'étape 9. L'interface de consentement SwiftUI (`SirDataCMPUI`, `ConsentView`, utilisée à l'étape 4) est livrée sous forme de sources Swift par Swift Package Manager et CocoaPods ; utilisez l'un des deux si vous voulez l'interface intégrée.

### Deux modules

Quel que soit le canal, vous importez deux modules :

```swift
import SirDataCMP    // API du cœur : IosCMPApi, handlers TCF / GPP / USP
import SirDataCMPUI  // interface de consentement SwiftUI : ConsentView
```

Ils sont séparés parce que le framework binaire possède le nom de module `SirDataCMP`.

Si votre application lie Firebase Analytics par Swift Package Manager, lisez [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md) avant d'aller plus loin : l'adaptateur de consentement a besoin que les classes de Firebase soient liées.

## 3. Initialiser le SDK

Initialisez le plus tôt possible, **avant tout SDK publicitaire ou analytics** : un SDK partenaire qui lit le stockage avant que la CMP ne se soit déclarée se comporte comme si aucune CMP n'était présente.

Trois règles :

* **Exactement une fois.** En 2.0.0, un second `initialize` pendant que le SDK tourne termine l'application (la prochaine version l'ignore et le journalise). Pour passer à un autre partenaire ou à une autre configuration, appelez d'abord `shutdown()` ; `IosCMPApi.shared.isInitialized()` vous dit s'il tourne.
* **Sur le fil principal.** `App.init()` et `application(_:didFinishLaunchingWithOptions:)` s'y exécutent tous les deux. Un appel depuis un autre fil est journalisé, et termine l'application sous `debugMode` (ci-dessous).
* **Avant tout autre appel au SDK.** En 2.0.0, ceux-ci terminent l'application quand le SDK n'est pas initialisé : présenter `ConsentView`, `getConsentState()`, `getConsentData()`, `getConsentUpdates()`, `getGoogleConsentState()`, `getLastForwardedConsentState()`, `getRegisteredAdapters()`, `getLastTcStringError()`, `discardPendingChanges()`, `acceptAll()`, `rejectAll()`, `saveConsent()` et `submitConsent(userAction:)`.

En 2.0.0, sur iOS, un mauvais usage termine l'application au lieu de lever une erreur : le SDK est écrit en Kotlin, et ses exceptions ne passent pas en Swift. La prochaine version (non publiée) le supprime : avant `initialize`, les getters renvoient une valeur neutre (rien de sélectionné, rien d'accordé, `nil`), `acceptAll()`, `rejectAll()`, `saveConsent()` et `submitConsent(userAction:)` lèvent une erreur Swift que vous pouvez attraper, un second `initialize` est ignoré et journalisé, et `ConsentView` affiche un message avec un bouton Fermer. Les trois règles restent la bonne intégration.

Les extraits utilisent le partenaire `1020` et la configuration `"hmDnl"`, une configuration d'exemple. Remplacez-les par les identifiants de votre application, disponibles dans la Console Sirdata.

### Application SwiftUI

```swift
import SwiftUI
import SirDataCMP

@main
struct MyApp: App {
    init() {
        IosCMPApi.shared.initialize(partnerId: 1020, configId: "hmDnl")
    }

    var body: some Scene {
        WindowGroup {
            RootView()   // la vue de l'étape 4
        }
    }
}
```

### Application UIKit

```swift
import UIKit
import SirDataCMP

@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        IosCMPApi.shared.initialize(partnerId: 1020, configId: "hmDnl")
        return true
    }
}
```

`partnerId` est un `Int32` en Swift : écrivez `Int32(id)` si vous le détenez sous forme d'`Int`.

`IABTCF_CmpSdkID` est écrit dans `UserDefaults.standard` **de façon synchrone** pendant cet appel, avant tout travail réseau, comme l'exige la spécification IAB. Tout le reste — configuration éditeur, listes de partenaires, consentement stocké — se charge ensuite, de façon asynchrone.

### Options de configuration

```swift
import SirDataCMP

func startConsentManagement() {
    let config = IosCMPConfig.Builder(partnerId: 1020, configId: "hmDnl")
        .gdprDefaultApplies(gdprDefaultApplies: true)
        #if DEBUG
        .debugMode(debugMode: true)
        #endif
        .build()
    IosCMPApi.shared.initialize(config: config)
}
```

* `gdprDefaultApplies` (par défaut `true`) ne sert qu'avant le chargement de la configuration, et seulement quand la locale de l'appareil ne porte pas de région. Une fois la configuration chargée, c'est l'API Sirdata qui décide si le RGPD s'applique.
* `debugMode` (par défaut `false`) fait terminer l'application à un appel de `initialize`, `shutdown` ou `setCcpaOptOut(optedOut:)` fait hors du fil principal, au lieu de seulement le journaliser, pour que le défaut apparaisse dans vos propres builds de debug. Il n'a pas d'autre effet. Gardez-le sous `#if DEBUG`.
* `apiRoot`, `cacheApiRoot` et `consentApiRoot` désignent par défaut les hôtes Sirdata (`api.`, `choices.` et `mychoice.consentframework.com`). Laissez-les tels quels.
* `language(language:)` est acceptée mais **sans effet** en 2.0.0. La langue de l'interface est celle que fixe votre configuration ; quand elle n'en fixe aucune, la langue préférée de l'appareil si l'API Sirdata la sert, puis la langue par défaut de la configuration, puis l'anglais.

Il n'y a pas d'option de thème sur iOS : couleurs, logo et position de la bannière viennent de votre configuration. Quand l'appareil est en mode sombre, les couleurs du mode sombre de la configuration s'appliquent si elle en définit.

## 4. Présenter l'interface de consentement

Laissez le SDK décider s'il faut la présenter. Ne testez **pas** la chaîne TC vous-même : un consentement partiel doit rouvrir la bannière, une fenêtre de capping active doit l'empêcher, et ni l'un ni l'autre ne se lit dans la chaîne.

### SwiftUI

```swift
import SwiftUI
import SirDataCMP
import SirDataCMPUI

struct RootView: View {
    @State private var showConsent = false
    @State private var workflow: Workflow?

    var body: some View {
        Text("Votre app")
            .sheet(isPresented: $showConsent) {
                ConsentView(onFinish: { showConsent = false }, workflow: workflow)
            }
            .task {
                guard let decision = try? await IosCMPApi.shared.awaitConsentDisplayDecision(),
                      decision.shouldDisplay else { return }
                workflow = decision.workflow
                IosCMPApi.shared.notifyBannerShown()  // émet `cmpuishown`
                showConsent = true
            }
    }
}
```

`awaitConsentDisplayDecision()` attend la configuration éditeur, puis applique la même cascade que la CMP web : applicabilité du RGPD, consentement stocké, cookie wall, les trois fenêtres de capping, et si le choix stocké couvre encore chaque finalité, partenaire et finalité éditeur actifs. Elle renvoie aussi *comment* présenter : un choix qui ne manque qu'au niveau des partenaires ouvre la bannière en mode « appliquer les choix », qui ne demande que ce qui reste à décider. Avec le cookie wall activé dans votre configuration, la bannière revient à chaque lancement tant que l'utilisateur n'a pas accordé la finalité 1.

Sur un réseau mort, l'attente dure jusqu'à ce que l'initialisation abandonne, ce qui peut prendre la somme des timeouts HTTP. Derrière un écran de lancement, bornez-la — passé l'échéance, vous obtenez la décision en l'état, provisoire si la configuration n'est pas arrivée (*fragment*) :

```swift
let decision = try await IosCMPApi.shared.awaitConsentDisplayDecision(timeoutMillis: 3_000)
```

`ConsentView` affiche tout le parcours (bannière → finalités → partenaires), habillé selon votre configuration. Son initialiseur est `ConsentView(onFinish:initialScreen:workflow:)` : `onFinish` est appelé quand l'utilisateur a terminé, et `initialScreen: .purposes` ouvre directement sur l'écran des préférences. La vue émet elle-même `cmpuiclosed` à sa fermeture. Appelez `notifyBannerShown()` chaque fois que vous la présentez.

### UIKit

Présentez `ConsentView` dans un `UIHostingController`, en plein écran sur un fond transparent : la vue dessine son propre fond assombri et positionne elle-même la bannière.

```swift
import SwiftUI
import UIKit
import SirDataCMP
import SirDataCMPUI

final class HomeViewController: UIViewController {
    private var didCheckConsent = false

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        guard !didCheckConsent else { return }
        didCheckConsent = true
        Task { @MainActor in
            guard let decision = try? await IosCMPApi.shared.awaitConsentDisplayDecision(),
                  decision.shouldDisplay else { return }
            presentConsent(workflow: decision.workflow)
        }
    }

    func presentConsent(workflow: Workflow?) {
        weak var host: UIHostingController<ConsentView>?
        let controller = UIHostingController(
            rootView: ConsentView(onFinish: { host?.dismiss(animated: true) }, workflow: workflow)
        )
        host = controller
        controller.modalPresentationStyle = .overFullScreen
        controller.view.backgroundColor = .clear
        IosCMPApi.shared.notifyBannerShown()  // émet `cmpuishown`
        present(controller, animated: true)
    }
}
```

Avant de livrer cette présentation plein écran, lisez le problème connu de 2.0.0 à la fin de la section suivante : elle n'a aucune issue quand la configuration n'a pas pu être chargée.

### Fermer la feuille sans répondre

Dans une `.sheet`, l'utilisateur peut faire glisser la bannière vers le bas. Cela la ferme **sans rien enregistrer** : le SDK le signale à Sirdata comme une fermeture sans choix, émet `cmpuiclosed`, et la bannière s'affiche de nouveau au lancement suivant. Pour l'empêcher — avec un cookie wall, par exemple —, désactivez le geste sur la vue (*fragment*) :

```swift
ConsentView(onFinish: { showConsent = false }, workflow: workflow)
    .interactiveDismissDisabled()
```

La présentation plein écran UIKit ci-dessus n'a pas ce geste.

**Problème connu de 2.0.0 : aucune issue tant que la configuration manque.** Si la configuration n'a pas pu être chargée à ce lancement — l'appareil est hors ligne, par exemple —, la décision peut quand même demander la bannière (raison `configNotLoaded`), mais `ConsentView` n'affiche alors qu'un indicateur de chargement, sans bouton de fermeture. Dans une simple `.sheet`, l'utilisateur peut encore la faire glisser vers le bas. Avec `.interactiveDismissDisabled()`, ou dans la présentation plein écran UIKit ci-dessus, rien ne la ferme. Si vous utilisez l'une ou l'autre, ne présentez la vue qu'une fois la configuration chargée. La prochaine version (non publiée) corrige cela : `ConsentView` affiche un message et un bouton Fermer quand la configuration n'a pas pu être chargée, et un bouton Fermer sous l'indicateur de chargement après cinq secondes. Avec UIKit, faites-en la première ligne de `presentConsent(workflow:)`, pour qu'elle couvre aussi la réouverture de l'étape 6 (*fragment*) :

```swift
guard IosCMPApi.shared.tcfApi().isCmpLoaded() else { return }
```

## 5. Lire le consentement dans votre code

### Valeurs

```swift
import SirDataCMP

func readConsent() {
    let cmp = IosCMPApi.shared
    let tcString    = cmp.getTcString()                    // "" tant qu'aucun choix n'est enregistré
    let gdprApplies = cmp.isGdprApplies()
    let analyticsOK = cmp.hasPurposeConsent(purposeId: 8)  // finalité TCF 8
    let googleOK    = cmp.hasVendorConsent(vendorId: 755)  // partenaire TCF 755
    let gppString   = cmp.getGppString()
    let acString    = cmp.getAddtlConsent()                // Google Additional Consent
    print(tcString, gdprApplies, analyticsOK, googleOK, gppString, acString)
}
```

Ces getters lisent les valeurs stockées, celles que lisent aussi les SDK partenaires : les clés IAB de `UserDefaults.standard` (`IABTCF_…`, `IABGPP_…`, et `IABUSPrivacy_String` dès qu'un choix US existe). [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md) les liste. `isGdprApplies()` est la réponse de l'API Sirdata une fois la configuration chargée, et une estimation d'après la région de l'appareil avant.

Certaines clés existent avant tout choix. `initialize` écrit `IABTCF_CmpSdkID`, `IABTCF_CmpSdkVersion` et `IABTCF_PolicyVersion`. Une fois la configuration chargée, le SDK écrit `IABTCF_gdprApplies`, de nouveau `IABTCF_PolicyVersion` (tirée de la liste de partenaires chargée), `IABTCF_PublisherCC`, `IABTCF_PurposeOneTreatment`, `IABTCF_UseNonStandardTexts`, les chaînes de bits éditeur (`IABTCF_PublisherConsent`, `IABTCF_PublisherLegitimateInterests` et les deux clés `IABTCF_PublisherCustomPurposes…`) et, pour un utilisateur américain, les clés GPP avec la section US. `IABTCF_TCString` et les consentements par finalité et par partenaire (`IABTCF_PurposeConsents`, `IABTCF_VendorConsents`…) n'apparaissent qu'une fois un choix enregistré.

### Réagir aux changements

Un flow Kotlin ne se collecte pas depuis Swift : observez le compteur de mises à jour du SDK avec un `UpdateCounterWatcher`. Créez-le après `initialize`, gardez une référence vers lui, et fermez-le quand vous avez terminé.

```swift
import SirDataCMP

final class ConsentGate {
    private var watcher: UpdateCounterWatcher?

    func start() {
        apply()  // le watcher ne se déclenche pas pour la valeur courante
        let watcher = UpdateCounterWatcher(flow: IosCMPApi.shared.getConsentUpdates())
        watcher.watch { [weak self] _ in self?.apply() }
        self.watcher = watcher
    }

    func stop() {
        watcher?.close()
        watcher = nil
    }

    private func apply() {
        let allowed = IosCMPApi.shared.hasPurposeConsent(purposeId: 1)
        print("purpose 1 granted:", allowed)  // activez ou désactivez ici le SDK qui en dépend
    }
}
```

Le callback s'exécute sur le fil principal. Il se déclenche à chaque changement de la sélection, y compris les bascules dans l'interface de consentement ouverte qui ne sont pas encore enregistrées : relisez donc les valeurs stockées à chaque fois plutôt que de compter les appels ; elles ne changent que quand l'utilisateur enregistre. `UserDefaults.didChangeNotification` fonctionne aussi, mais se déclenche à chaque écriture dans les defaults de l'application.

Un choix US — enregistré sur l'écran US, ou avec `setCcpaOptOut(optedOut:)` — ne fait pas avancer le compteur : relisez `getUspString()` et `getGppString()` à la fermeture de l'interface de consentement (dans `onFinish`), et juste après vos propres appels à `setCcpaOptOut`.

L'API TCF in-app (`tcfApi()`) a une commande `addEventListener`, mais en 2.0.0 son callback ne dit pas quel événement a eu lieu : relisez-y aussi les valeurs stockées.

### Google Consent Mode

`IosCMPApi.shared.getGoogleConsentState()` renvoie les signaux Google que le SDK dérive du choix TCF stocké (`adStorageGranted`, `analyticsStorageGranted`, `adUserDataGranted`, `adPersonalizationGranted`). Le SDK transmet lui-même le consentement à Firebase Analytics : voir [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md).

### Votre propre écran de consentement

À la place de `ConsentView`, un écran à vous peut enregistrer un choix avec `try await IosCMPApi.shared.acceptAll()` ou `rejectAll()`. Chacune renvoie un `ConsentResult` : `ConsentResult.Success`, ou `ConsentResult.Error` avec un `message`. Appelez `notifyBannerShown()` quand votre écran apparaît et `notifyBannerClosed()` quand il se ferme, pour que l'API TCF in-app émette `cmpuishown` et `cmpuiclosed`. `recordAskLater()` enregistre un « me le redemander plus tard » : la bannière reste masquée pendant une heure. Chaque méthode figure dans la [Référence](/applications-mobiles-sdk-natif/mobile-sdk/reference-1.md).

## 6. Rouvrir depuis un écran de réglages

Les utilisateurs doivent pouvoir modifier leurs choix à tout moment. Donnez-leur un point d'entrée — une ligne « Choix de confidentialité » dans vos réglages — qui présente la vue dans le workflow manuel :

```swift
import SwiftUI
import SirDataCMP
import SirDataCMPUI

struct PrivacyChoicesRow: View {
    @State private var showConsent = false

    var body: some View {
        Button("Choix de confidentialité") {
            IosCMPApi.shared.notifyBannerShown()
            showConsent = true
        }
        .sheet(isPresented: $showConsent) {
            ConsentView(onFinish: { showConsent = false }, workflow: .manualDisplay)
        }
    }
}
```

Avec UIKit, appelez `presentConsent(workflow: .manualDisplay)` de l'étape 4.

Dans le workflow manuel, la bannière a une croix de fermeture en haut et un bouton « Tout refuser », quels que soient le type et le style de refus que fixe votre configuration, et les écrans finalités et partenaires montrent les choix enregistrés. À son apparition, la vue relance la fenêtre de capping de votre configuration et signale une réouverture (`load:revisit`) : n'appelez pas `recordPrompt()` vous-même.

## 7. Utilisateurs américains

Quand le RGPD ne s'applique pas, le SDK ne demande jamais à afficher la bannière : `awaitConsentDisplayDecision()` renvoie `shouldDisplay == false`, avec la raison `gdprNotApplies`. Un utilisateur américain ne voit donc rien tant que votre application n'ouvre pas l'interface de consentement.

`isCcpaApplies()` vous dit si le régime US s'applique : la réponse de l'API Sirdata une fois la configuration chargée, la région de l'appareil avant. Quand il vaut `true`, `ConsentView` affiche l'écran US du SDK à la place de la bannière TCF, quel que soit le workflow. L'enregistrer enregistre le choix d'opposition de l'utilisateur : le SDK écrit `IABUSPrivacy_String` et réencode les sections US de la chaîne GPP. Jusque-là, `IABUSPrivacy_String` est absente ; les clés GPP, avec la section US, sont écrites dès que la configuration est chargée, avec des valeurs par défaut tant que l'utilisateur n'a pas choisi. Un choix US ne fait pas avancer le compteur de `getConsentUpdates()` (étape 5).

Montrez donc le point d'entrée de l'étape 6 aux utilisateurs américains aussi. Si vous construisez plutôt votre propre écran US, appelez `IosCMPApi.shared.setCcpaOptOut(optedOut:)` sur le fil principal, et seulement avec le choix que l'utilisateur a réellement fait — jamais à sa place.

## 8. Tester et vérifier l'intégration

Pour rejouer un premier lancement, supprimez l'application, ou appelez `IosCMPApi.shared.clearConsent()` et relancez. Appelez-le aussi chaque fois que vous passez à un autre partenaire ou à une autre configuration : un choix stocké appartient à l'éditeur qui l'a collecté.

L'application du RGPD est décidée par l'API Sirdata, d'après le pays de l'adresse IP de l'appareil et votre configuration — pas d'après les réglages de région de l'appareil. Là où le RGPD ne s'applique pas, la bannière ne s'affiche pas (raison `gdprNotApplies`).

Dans un build de debug, vérifiez chaque point dans l'ordre :

1. Juste après `initialize`, `UserDefaults.standard.integer(forKey: "IABTCF_CmpSdkID")` vaut `92`.
2. Au premier lancement, la décision a `shouldDisplay == true` et la bannière apparaît avec les textes, les couleurs et le logo de votre configuration. Les écrans finalités et partenaires s'ouvrent. Avant tout choix, une fois la configuration chargée, `IABTCF_gdprApplies` vaut `1` et `IABTCF_TCString` est absente.
3. Après **Tout accepter**, `IABTCF_TCString`, `IABTCF_PurposeConsents`, `IABTCF_VendorConsents` et `IABGPP_HDR_GppString` sont renseignées, et `IABUSPrivacy_String` est absente.
4. Après une relance, la bannière ne s'affiche plus.
5. La réouverture de l'étape 6 montre la croix de fermeture, le bouton « Tout refuser » et les choix enregistrés ; un nouveau choix remplace l'ancien.
6. `ping` réussit et indique `cmpStatus` `loaded`.
7. La console Xcode n'affiche aucune ligne commençant par `[CMP]`. Il n'en apparaît une que lorsque quelque chose est à corriger : un appel hors du fil principal, un adaptateur de consentement qui n'a pas pu transmettre un choix, une chaîne TC stockée qui n'a pas pu être relue.
8. Un build Release tourne sur un vrai appareil.

Les clés des points 1 à 3, et le point 6, se vérifient depuis le code (*fragment*). Lisez `IABTCF_CmpSdkID` juste après `initialize` ; lancez le reste une fois la configuration chargée — depuis un bouton de debug, par exemple : jusque-là, `ping` répond avec `cmpStatus` `stub`.

```swift
print("CmpSdkID:", UserDefaults.standard.integer(forKey: "IABTCF_CmpSdkID"))

let keys = UserDefaults.standard.dictionaryRepresentation().keys
    .filter { $0.hasPrefix("IABTCF_") || $0.hasPrefix("IABGPP_") || $0 == "IABUSPrivacy_String" }
    .sorted()
print("IAB keys:", keys)

IosCMPApi.shared.tcfApi().execute(command: "ping", version: 2, callback: { data, _, success in
    let ping = data as? [String: Any]
    print("ping:", success.boolValue, ping?["cmpStatus"] ?? "none")
}, parameter: nil)
```

Si la bannière n'apparaît jamais, la décision le dit avant tout log (*fragment*) :

```swift
let decision = try await IosCMPApi.shared.awaitConsentDisplayDecision()
print("display:", decision.shouldDisplay, "workflow:", decision.workflow.name,
      "reason:", decision.reason.name)
```

Une fenêtre de capping active (`cappingActive`, `cappingAskLater`), un consentement stocké qui couvre encore tout (`consentOk`) ou un RGPD non applicable (`gdprNotApplies`) sont des raisons normales pour que la bannière ne s'affiche pas.

Si la configuration ne se charge pas — `IosCMPApi.shared.tcfApi().isCmpLoaded()` reste à `false` quelques secondes après le lancement —, `IosCMPApi.shared.getInitError()` renvoie le message d'erreur du chargement échoué ; un partner ID ou un config ID inconnu et une panne réseau y aboutissent tous les deux. Le SDK ne le journalise pas. Lisez-le à ce moment-là, pas dès le retour d'`awaitConsentDisplayDecision()` : en 2.0.0, à cet instant, il peut encore valoir `nil` après un échec. La prochaine version le renseigne avant le retour d'`awaitConsentDisplayDecision()`. Voir aussi [Logs](/applications-mobiles-sdk-natif/mobile-sdk/logging.md) et [Dépannage](/applications-mobiles-sdk-natif/mobile-sdk/troubleshooting.md).

## 9. Préparer la release

* **Vos propres identifiants.** Remplacez les partner ID et config ID d'exemple par ceux de votre application.
* **`debugMode` dans les builds de debug uniquement.** Il termine l'application sur une violation du fil principal, et le SDK ne regarde pas le type de build : gardez-le sous `#if DEBUG`.
* **Licence.** Livrer l'application exige un abonnement valide et en cours à la Sirdata Consent Management Platform ; un abonnement pris par la Console Sirdata suffit (étape 1).
* **Mentions des composants tiers.** Le SDK embarque des composants open source sous Apache License 2.0 et sous Boost Software License 1.0. Quand vous distribuez votre application, reproduisez le fichier de mentions du SDK et le texte de ces licences, et mettez-les à la disposition de vos utilisateurs — dans l'écran des remerciements de votre application, par exemple. C'est une condition de l'Apache License, et de la section 26.1(b) du `LICENSE` du SDK. Avec Swift Package Manager, ils sont dans le paquet, sous `NOTICE` et `THIRD-PARTY-LICENSES/` ; avec CocoaPods, dans `Pods/SirdataCMP/` sous `NOTICE-ios` et `THIRD-PARTY-LICENSES/` ; avec le XCFramework manuel, prenez-les dans le [dépôt du paquet](https://github.com/SirDataFR/sirdata-cmp-ios-spm) sur GitHub.
* **Manifeste de confidentialité.** Le SDK 2.0.0 ne livre pas de manifeste de confidentialité Apple (`PrivacyInfo.xcprivacy`). Il lit et écrit `UserDefaults.standard`, l'une des catégories d'API (`NSPrivacyAccessedAPICategoryUserDefaults`) que couvre la documentation d'Apple sur les [required reason APIs](https://developer.apple.com/documentation/bundleresources/describing-use-of-required-reason-api) (consultée le 2026-10-05). Tenez-en compte pour votre soumission à l'App Store, et posez vos questions à votre contact Sirdata.
* **Détails de confidentialité de l'app.** Les [privacy details](https://developer.apple.com/app-store/app-privacy-details/) que vous renseignez dans App Store Connect incluent les pratiques des SDK tiers de votre application (consulté le 2026-10-05), celui-ci compris. Il envoie des requêtes à `api.consentframework.com`, `choices.consentframework.com` et `mychoice.consentframework.com`.

## 10. Monter de version

**Depuis Swift Package Manager 1.2.0.** « Up to Next Major Version » ne franchit jamais une version majeure : une règle posée à partir de 1.2.0 reste en 1.x. Faites-la partir de 2.0.0 : dans Xcode, dans l'onglet **Package Dependencies** de votre projet ; dans un `Package.swift`, `from: "2.0.0"`. Relevez ensuite votre deployment target à iOS 15.0 s'il est plus bas. Aucune API publique n'a été supprimée ni renommée en 2.0.0 : votre code n'a besoin d'aucun changement ; [Versions](/applications-mobiles-sdk-natif/mobile-sdk/versions-1.md) liste ce qui a changé de comportement.

**Depuis le pod `SirdataCMP` de 2020 (1.x).** C'est un autre SDK, construit à partir d'une autre base de code. Retirez-le, ainsi que le code qui l'appelle, puis intégrez ce SDK à partir de l'étape 2, avec `pod 'SirdataCMP', '~> 2.0'`.

**Gardez les deux modules à la même version.** La couche SwiftUI et le framework binaire sont publiés ensemble, et les deux gestionnaires de paquets les gardent alignés. Si Xcode signale qu'un type n'a pas un membre qu'utilise le code du SDK, résolvez à nouveau le paquet, et ne mélangez pas une copie locale avec une version publiée.

## Pour continuer

* [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md) — la partie qui fait fonctionner la publicité
* [Référence](/applications-mobiles-sdk-natif/mobile-sdk/reference-1.md) — chaque méthode et chaque événement
* [Logs](/applications-mobiles-sdk-natif/mobile-sdk/logging.md) — ce que le SDK écrit dans la console
* [Dépannage](/applications-mobiles-sdk-natif/mobile-sdk/troubleshooting.md) — les erreurs que les intégrateurs rencontrent réellement
* [Versions](/applications-mobiles-sdk-natif/mobile-sdk/versions-1.md) — ce qui a changé à chaque version


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://cmp.docs.sirdata.net/applications-mobiles-sdk-natif/mobile-sdk/setup-1.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
