For the complete documentation index, see llms.txt. This page is also available as Markdown.

iOS — Versions

Cette page est maintenue en anglais — une seule source évite qu'une traduction dérive de la réalité du SDK à chaque release.

All notable changes to the iOS SDK. Entries are newest first.

Sections follow the same headings throughout: Features for new or changed behaviour, Fixes for corrections and, from 2.0.0, Requirements at the end. Breaking changes are called out in their own block at the top of the entry.

Maintainers: a new entry is dated on the day the version is actually published, not when it is written. An entry marked (unreleased) has not been tagged yet — see the release checklist.

Next release (unreleased)

Fixes

  • A call made before initialize no longer terminates the app. The getters return a neutral value: getConsentState() a flow that stays LOADING, getConsentUpdates() a flow that stays at 0, getConsentData() an empty selection, getGoogleConsentState() every signal denied, getLastTcStringError() and getLastForwardedConsentState() nil, getRegisteredAdapters() an empty list, and discardPendingChanges() does nothing. acceptAll(), rejectAll(), saveConsent() and submitConsent(userAction:) throw a Swift error instead. Their Objective-C selectors are unchanged.

  • A second initialize without shutdown() is ignored and logged ([CMP] line in the Xcode console), instead of terminating the app. The running session is kept.

  • ConsentView always offers a way out. When the configuration could not be loaded at this launch (offline, unknown config ID), it shows a message and a Close button instead of a loading indicator that could not be closed in a full-screen presentation or with .interactiveDismissDisabled(). While the configuration loads, a Close button appears under the indicator after five seconds. Created before initialize, it shows the same message instead of terminating the app.

  • getInitError() is set when getConsentState() turns READY, so it can be read as soon as awaitConsentDisplayDecision() returns, including in its completion handler. In 2.0.0 it could still be nil at that moment after a failed load.

2.0.0 (2026-10-05)

First version published on CocoaPods, as the pod SirdataCMP, alongside Swift Package Manager. The version number moves to 2.0.0 on every channel at once: the 1.x versions of that pod name are an older, discontinued Sirdata SDK.

Breaking changes

  • iOS 15.0 minimum, Swift Package Manager included — the SPM package 1.2.0 declared iOS 14.0. Xcode 27 no longer builds a deployment target below 15.0.

Features

  • The consent adapters forward each choice to Firebase Analytics, AppsFlyer, Adjust and Branch, from inside the SDK, through the Objective-C runtime. In 1.2.0 the forwarding was compiled out for every Swift Package Manager and CocoaPods integrator, only ran with the bundled UI, and called APIs these SDKs do not have. It now works with both package managers, with or without the bundled UI, and a failing adapter is logged in the Xcode console, prefixed with [CMP]. With Swift Package Manager, Firebase requires the -ObjC linker flag. See Sharing consent with vendor SDKs.

  • "TCF/Google Consent Mode Interoperability" (consentModeFromTcf) behaves as on the web. The SDK writes IABTCF_EnableAdvertiserConsentMode = 1, ping and getTCData carry enableAdvertiserConsentMode: true, and Firebase Analytics receives analytics_storage only, so that it derives the three ad signals from the TC string.

  • Publisher restrictions are applied. The consent UI no longer asks a partner for a purpose the publisher forbids it, the TC string and the IABTCF_PublisherRestrictions{ID} keys carry the restrictions, and the signals sent to Google through Firebase and AppsFlyer take them into account.

  • The Sirdata consent string carries the restrictions the configuration applies to Sirdata partners, and the version of the Sirdata list it was built from (it always said 1).

  • The three non-versioned lists (Global Vendor List, Sirdata list, Google Additional Consent providers) are requested with the visitor's country, when it is known, so the API can filter them.

  • The consent archive carries the country and the revision of the configuration that was displayed. Every hit carries a page_view_id, drawn once per initialize(), so the load, the clicks and the answer of one display share it.

  • The banner's refusal button follows the publisher's style as the web does. "Close link" puts the refusal label in the header, in place of the close cross (it was a grey cross without a label, on its own line); "Big button (secondary color)" is outlined like "Customize my choices" (it was a solid grey block); "Big button (main color)" looks exactly like "Accept all" (it was drawn at 70 % opacity). The manual re-opening shows a "Reject all" button whatever the refusal type and style, with the close cross at the top; it showed no refusal at all.

  • The purposes and partners screens carry the links of their scope reminder, as on the web: the list of the sites the consent covers, and the Consent Framework site.

  • Outside the US screen, the close button that saves nothing reports its answer, response:close, along with its click, ui:close, as the web does; 1.2.0 sent the click alone. The US screen's close button still reports ccpa_response:close alone. Under the CNIL variant, where the close button saves the choice, nothing changes.

  • Dismissing the consent sheet by swiping it down is reported like the close button that saves nothing (response:close and ui:close, or ccpa_response:close on the US screen), and fires cmpuiclosed. Any removal of ConsentView from the screen that does not go through one of its buttons counts as such a dismissal — a view pushed over it, for instance.

  • isGdprApplies() and the in-app TCF API return context.gdprApplies as served by the API; the SDK no longer adds forceGdpr on top of it. The server already folds forceGdpr into that value, so what an integrator sees does not change.

Fixes

  • The SDK no longer fails to start on a configuration that serves publisher restrictions. The API serves restrictionType as a number, which 1.2.0 could not decode: initialize() failed and the CMP never loaded.

  • The publisher's text size is applied. It was computed and never read. It now shifts Dynamic Type by one step down ("small") or up ("large"), so the system's accessibility text size keeps working.

  • Toggling a stack records only the members the screen shows, and a purpose row records only the legal bases the served list asks for. Both could record a consent or a legitimate interest the user was never asked for.

  • Utils.buildAddtlConsent(customData:) returns the string the SDK stores in IABTCF_AddtlConsent, prefixed 2~. It returned a 1~ string that no device ever stored.

  • In dark mode, a logo or watermark set to "no image" no longer displays the word NONE, and an empty dark-mode image falls back to the light-mode one, as on the web.

  • A long refusal label ("Continuer sans accepter") wraps onto two lines instead of being truncated, and a banner shown at the bottom of the screen extends its background under the home indicator.

Requirements

  • iOS 15.0 or later, whatever the channel; Swift 5.9 or later (Xcode 15 or later); verified with Xcode 27.0. See Setup — iOS, step 1.

1.2.0 (2026-09-23)

First version distributed through a package channel other than a raw git tag: Swift Package Manager resolves it from an XCFramework attached to the GitHub release, verified by its SHA-256 checksum. The pod announced for this version was never published: the CocoaPods trunk refused it, because pod names are case-insensitive and SirdataCMP, an older Sirdata SDK, already holds the name. The channel opens with 2.0.0, under that name.

Breaking changes

  • The setGdprApplies command is removed from the in-app TCF API. It duplicated forceGdpr, which stays the single supported way to override GDPR applicability. Callers using setGdprApplies must switch to forceGdpr.

  • Two public symbols are removed from the Kotlin framework: TcStringEncoder.convertTcStringToGppString(tcString:) and Context.isAskLaterCappingActive(store:). Neither has ever been reachable from a published artifact, and both were dead in the SDK itself: the first built a GPP string carrying the TCF section alone, which stopped being the production path when the US National section arrived (the string and IABGPP_GppSID are now assembled together from one section list); the second duplicated the "ask later" capping rule with a different window — context.cappingInHours instead of the fixed one hour. Read IABGPP_HDR_GppString for the GPP string, and call awaitConsentDisplayDecision() to ask whether the banner should be shown.

Features

  • The main-thread contract of initialize, shutdown and setCcpaOptOut is enforced rather than only documented. The three read and re-encode the consent state — jurisdiction, configuration and store — together and without a lock, so calling them from a background thread is a race. Off the main thread the SDK now logs the violation, naming the entry point; declare debugMode(true) on the configuration and it throws instead, so the fault surfaces in your own debug build rather than as a rare, silent race in production. debugMode has no other effect.

  • The SDK's language coverage goes from 18 to 50 — every locale the Sirdata API serves. A device set to one of the 32 missing languages previously fell back to the partner's default language, because getDeviceLanguage() could not represent it. BCP 47 tags are now matched with their full label and case-insensitively, the way NSLocale.preferredLanguages actually reports them.

  • Right-to-left languages render right-to-left. The consent UI had no layout direction handling at all and inherited the host app's reading direction: an Arabic app displaying a French banner mirrored it, and a French app displaying an Arabic banner did not. The UI now sets its own direction from the banner's language — LTR as explicitly as RTL, which is what closes the inheritance for the 48 left-to-right languages.

  • Norwegian and Filipino are reachable: devices report nb-NO, nn-NO or fil-PH, none of which matched the no / tl codes the API serves. An alias table maps them; the failure was silent — no error, no log, just a banner in the wrong language.

  • The consent UI no longer lists a partner for which the user has no decision to make — a partner with neither a consent nor a legitimate-interest toggle was previously rendered as an inert row.

  • Version alignment: the SDK, the pod and the React Native bridge now declare and resolve the same version. Earlier numbering diverged across the podspec and the Gradle module.

  • GPP US National (section 7) is emitted alongside the deprecated uspv1 section, and IABGPP_GppSID is built from the same section list as the string rather than derived from it — it used to be read back out of a Fibonacci-encoded header, which cannot work, so it never announced anything but 2.

  • The publisher configuration is now fetched from the cacheApiRoot host (choices.consentframework.com by default) rather than apiRoot (api.consentframework.com). Both hosts serve the same endpoint, and the two responses were compared key by key: 614 keys against 615, no differing value, the single extra key being the context.region that only choices can carry — it sits behind the CDN that resolves the visitor's country and US state, which the US National section needs. Integrators who pin hosts in a network allowlist, a proxy rule or a certificate-pinning configuration should confirm cacheApiRoot is reachable — the other two hosts are unchanged, and all three remain configurable.

  • getUspString() returns an empty string when no opt-out decision has been recorded. "1---" is not a neutral default: the US Privacy spec makes it an explicit assertion that the consumer falls outside a US Privacy jurisdiction. The sale opt-out itself is recorded under its own key, so a value fabricated by an earlier SDK version can never be mistaken for a decision the user made.

Fixes

  • ping no longer reports a GDPR determination it has not actually made. Until the publisher configuration is loaded, the command reports the undetermined state instead of a default that could be wrong, so vendor SDKs stop acting on a value the CMP had not computed.

  • getGdprApplies() no longer asserts that GDPR does not apply when the key is absent. On a fresh install, or after clearConsent(), NSUserDefaults returns 0 for a missing key exactly as for a stored zero — and 0 is not "undetermined", it is the firm claim that GDPR does not apply, which a vendor acts on without consent. Absence is now distinguished from a stored value, matching Android. This affected getTCData and the GPP tcfeuv2 section.

  • clearConsent() also removes the legacy IABTCF_* key names. They were still read as a fallback, so consent that had been cleared came back on the next launch.

  • The TCF policy version fallback lives in the storage reader rather than at each call site, so ping and getTCData can no longer report different values in the same session.

  • In the "apply choices" workflow, the refusal button is no longer rendered when the publisher set the no-consent button to NONE — that is, when they explicitly chose not to offer a refusal on the banner.

1.1.0 (2026-08-10)

Tagged in Sirdata's private source repository only: never resolvable from the public package repository, never published to the CocoaPods trunk, and not announced as generally available.

Features

  • IAB TCF v2.2 in-app CMP API (registered CMP ID 92), IAB GPP and CCPA/USP support, with all signals written to NSUserDefaults under the standard IAB keys.

  • Native SwiftUI consent UI (ConsentView): banner, purposes and partners screens, themed from the publisher configuration.

  • Display cascade ported from the web CMP — awaitConsentDisplayDecision() decides whether and how to present the UI, covering partial consent, capping windows and the cookie wall.

  • MANUAL_DISPLAY and APPLY_CHOICES workflows for re-opening the UI from a settings screen and for asking only what is left to decide.

  • Google Additional Consent string and Google Consent Mode state derived from TCF purposes.

Mis à jour

Ce contenu vous a-t-il été utile ?