# IOS - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: fr Generated on: 2026-08-11T07:13:23.473Z Total files: 53 --- # File: ios-sdk-overview --- --- title: "Présentation du SDK iOS" description: "Découvrez le SDK iOS d'Adapty et ses fonctionnalités clés." --- [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-iOS.svg?style=flat&logo=apple)](https://github.com/adaptyteam/AdaptySDK-iOS/releases) Bienvenue ! Nous sommes là pour simplifier vos achats intégrés 🚀 Nous avons conçu le SDK iOS d'Adapty pour vous libérer de la complexité des achats intégrés, afin que vous puissiez vous concentrer sur ce que vous faites le mieux : créer des applications formidables. Voici ce que nous gérons pour vous : - Traitement des achats, validation des reçus et gestion des abonnements prêts à l'emploi - Création et test de flows et de paywalls sans mise à jour de l'application - Analyses d'achats détaillées sans configuration — cohortes, LTV, churn et analyse d'entonnoir inclus - Statut d'abonnement utilisateur toujours à jour entre les sessions et les appareils - Intégration de votre application avec des services d'attribution marketing et d'analyse en une seule ligne de code :::note Avant de plonger dans le code, vous devez intégrer Adapty avec App Store Connect et configurer vos produits dans le tableau de bord. Consultez notre [guide de démarrage rapide](quickstart) pour tout configurer en premier. ::: ## Démarrer \{#get-started\} For a fully automated integration, use the [adapty-sdk-integration skill](https://github.com/adaptyteam/adapty-sdk-integration-skill): it runs the whole integration from your AI coding tool in one command. Voici ce que nous allons couvrir dans le guide d'intégration : 1. [Installer et configurer le SDK](sdk-installation-ios) : Ajoutez le SDK comme dépendance à votre projet et activez-le dans le code. 2. [Activer les achats via des flows](ios-quickstart-paywalls) : Configurez le flow d'achat pour que les utilisateurs puissent acheter des produits. Pour créer votre propre interface, consultez plutôt [Implémenter les paywalls manuellement](ios-quickstart-manual). 3. [Vérifier le statut d'abonnement](ios-check-subscription-status) : Vérifiez automatiquement l'état d'abonnement de l'utilisateur et contrôlez son accès au contenu payant. 4. [Identifier les utilisateurs (optionnel)](ios-quickstart-identify) : Associez les utilisateurs à leurs profils Adapty pour garantir que leurs données sont stockées de façon cohérente sur tous les appareils. ### Voir en action \{#see-it-in-action\} Vous voulez voir comment tout s'assemble ? Nous avons ce qu'il vous faut : - **Exemples d'applications** : Consultez nos [exemples complets](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples) qui illustrent la configuration complète ## Concepts principaux \{#main-concepts\} Avant de plonger dans le code, familiarisons-nous avec les concepts clés qui font fonctionner Adapty. L'approche d'Adapty repose sur un principe simple : seuls les placements sont codés en dur dans votre application. Tout le reste — produits, designs de paywalls, tarifs et offres — peut être géré de façon flexible depuis l'Adapty Dashboard sans mise à jour de l'application : 1. [**Produit**](product) - Tout ce qui est disponible à l'achat dans votre application : abonnement, produit consommable ou accès à vie. 2. **Flow ou paywall** - Des produits regroupés avec une configuration, rattachés à un placement. Deux variantes : - **[Flow](adapty-flow-builder)** - Interface visuelle sans code, créée dans le Flow Builder. Adapty affiche l'interface et gère l'achat pour vous. - **[Paywall](paywalls)** - Pas de configuration visuelle ; vous créez l'interface dans votre propre code et appelez `makePurchase` vous-même. Voir [Implémenter les paywalls manuellement](ios-quickstart-manual). Dans le code du SDK, les deux sont récupérés via la même méthode `getFlow`. 3. [**Placement**](placements) - Un point stratégique dans le parcours utilisateur où vous souhaitez afficher un flow ou un paywall. Considérez les placements comme le « où » et le « quand » de votre stratégie de monétisation. Les placements courants comprennent : - `main` - L'emplacement principal de votre paywall - `onboarding` - Affiché pendant le flow d'onboarding de l'utilisateur - `settings` - Accessible depuis les paramètres de votre application Commencez par les bases comme `main` ou `onboarding` pour votre première intégration, puis [réfléchissez aux autres endroits de votre application où les utilisateurs pourraient être prêts à acheter](choose-meaningful-placements). 4. [**Profil**](profiles-crm) - Lorsque les utilisateurs achètent un produit, leur profil se voit attribuer un **niveau d'accès** que vous utilisez pour définir l'accès aux fonctionnalités payantes. --- # File: sdk-installation-ios --- --- title: "Installer et configurer le SDK iOS" description: "Guide étape par étape pour installer le SDK Adapty sur iOS pour les applications à abonnement." --- Le SDK Adapty comprend deux modules clés pour une intégration fluide dans votre application mobile : - **Core Adapty** : ce SDK essentiel est requis pour qu'Adapty fonctionne correctement dans votre application. - **AdaptyUI** : ce module optionnel est nécessaire si vous utilisez le [Adapty Paywall Builder](adapty-paywall-builder), un outil no-code convivial pour créer facilement des paywalls multiplateformes. :::tip Vous voulez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [applications exemples](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: Pour une présentation complète de l'implémentation, vous pouvez également regarder les vidéos :
## Prérequis \{#requirements\} Le SDK Adapty pour iOS nécessite iOS 15.0 ou version ultérieure. :::important Adapty SDK 3.15.7+ est requis lors de la compilation avec Xcode 26.4 ou version ultérieure. ::: :::info L'installation du SDK correspond à l'étape 5 de la configuration d'Adapty. Avant que les achats fonctionnent dans votre app, vous devez également connecter votre app aux stores, puis créer des produits, un paywall et un placement dans l'Adapty Dashboard. Le [guide de démarrage rapide](quickstart) décrit toutes les étapes requises. ::: ## Installer le SDK Adapty \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-iOS.svg?style=flat&logo=apple)](https://github.com/adaptyteam/AdaptySDK-iOS/releases) Le SDK Adapty s'installe via Swift Package Manager. Dans Xcode, allez dans **File** -> **Add Package Dependency...**. Notez que les étapes pour ajouter des dépendances de package peuvent varier selon les versions d'Xcode ; consultez la documentation Xcode si nécessaire. 1. Saisissez l'URL du dépôt : ``` https://github.com/adaptyteam/AdaptySDK-iOS.git ``` 2. Sélectionnez la version (la dernière version stable est recommandée) et cliquez sur **Add Package**. 3. Dans la fenêtre **Choose Package Products**, sélectionnez les modules dont vous avez besoin : - **Adapty** (module principal) - **AdaptyUI** (facultatif - uniquement si vous prévoyez d'utiliser Paywall Builder) :::note Remarque : - Pour activer le [Mode Enfants](kids-mode) dans SDK 3.x, sélectionnez **Adapty_KidsMode** à la place de **Adapty**. Dans SDK 4.0 et versions ultérieures, sélectionnez les modules habituels — le Mode Enfants est activé via le trait de package `KidsMode`. - Ne sélectionnez pas d'autres packages dans la liste — vous n'en aurez pas besoin. ::: 4. Cliquez sur **Add Package** pour terminer l'installation. 5. **Vérifiez l'installation :** dans le navigateur de projet, vous devriez voir « Adapty » (et « AdaptyUI » si sélectionné) sous **Package Dependencies**. ## Activer le module Adapty du SDK \{#activate-adapty-module-of-adapty-sdk\} Activez le SDK dans le code de votre application. :::note Le SDK n'a besoin d'être activé qu'une seule fois dans votre application. ::: Pour obtenir votre **Public SDK Key** : 1. Accédez à l'Adapty Dashboard et naviguez vers [**App settings → General**](https://app.adapty.io/settings/general). 2. Dans la section **Api keys**, copiez la **Public SDK Key** (et NON la Secret Key). 3. Remplacez `"YOUR_PUBLIC_SDK_KEY"` dans le code. Ou obtenez-la de façon programmatique via l'[Adapty CLI](developer-cli) : ``` npm install -g adapty adapty auth login adapty apps list ``` Ou, directement : ``` npx adapty auth login adapty apps list ``` - Assurez-vous d'utiliser la **Public SDK key** pour l'initialisation d'Adapty — la **Secret key** ne doit être utilisée que pour l'[API côté serveur](getting-started-with-server-side-api). - Les **SDK keys** sont propres à chaque application, donc si vous avez plusieurs applications, veillez à choisir la bonne. ```swift showLineNumbers @main struct YourApp: App { init() { // Configure Adapty SDK let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard Adapty.logLevel = .verbose // recommended for development and the first production release let config = configurationBuilder.build() // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } var body: some Scene { WindowGroup { // Your content view } } } } ``` ```swift showLineNumbers // In your AppDelegate class: // If you only use an AppDelegate, place the following code in the // application(_:didFinishLaunchingWithOptions:) method. // If you use a SceneDelegate, place the following code in the // scene(_:willConnectTo:options:) method. Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(logLevel: .verbose) // recommended for development and the first production release let config = configurationBuilder.build() try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` :::important Attendez que `activate` soit résolu avant d'appeler toute autre méthode du SDK Adapty. Consultez [l'ordre des appels dans le SDK iOS](ios-sdk-call-order) pour la séquence complète. ::: Configurez maintenant les paywalls dans votre application : - Si vous utilisez [Adapty Paywall Builder](adapty-paywall-builder), commencez par [activer le module AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) ci-dessous, puis suivez le [démarrage rapide avec Paywall Builder](ios-quickstart-paywalls). - Si vous créez votre propre interface de paywall, consultez le [démarrage rapide pour les paywalls personnalisés](ios-quickstart-manual). ## Activer le module AdaptyUI du SDK Adapty \{#activate-adaptyui-module-of-adapty-sdk\} Si vous prévoyez d'utiliser le [Paywall Builder](adapty-paywall-builder) et avez [installé le module AdaptyUI](sdk-installation-ios#install-adapty-sdk), vous devez également activer AdaptyUI. :::important Dans votre code, vous devez activer le module principal Adapty avant d'activer AdaptyUI. ::: ```swift showLineNumbers title="Swift" @main struct YourApp: App { init() { // ...ConfigurationBuilder steps // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: config) try await AdaptyUI.activate() } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } // main body... } } ``` ```swift showLineNumbers title="UIKit" // If you only use an AppDelegate, place the following code in the // application(_:didFinishLaunchingWithOptions:) method. // If you use a SceneDelegate, place the following code in the // scene(_:willConnectTo:options:) method. Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(logLevel: .verbose) // recommended for development let config = configurationBuilder.build() try await Adapty.activate(with: config) try await AdaptyUI.activate() } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` :::tip En option, lors de l'activation d'AdaptyUI, vous pouvez [remplacer les paramètres de mise en cache par défaut pour les paywalls](#media-cache-configuration-for-paywalls-in-adaptyui). ::: ## Configuration optionnelle \{#optional-setup\} ### Journalisation \{#logging\} #### Configurer le système de journalisation \{#set-up-the-logging-system\} Adapty enregistre les erreurs et autres informations importantes pour vous aider à comprendre ce qui se passe. Les niveaux suivants sont disponibles : | Level | Description | | ---------- | ------------------------------------------------------------ | | `error` | Seules les erreurs seront journalisées | | `warn` | Les erreurs et les messages du SDK qui ne causent pas d'erreurs critiques, mais qui méritent attention, seront journalisés | | `info` | Les erreurs, avertissements et divers messages d'information seront journalisés | | `verbose` | Toute information supplémentaire pouvant être utile lors du débogage, comme les appels de fonctions, les requêtes API, etc. sera journalisée | ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(logLevel: .verbose) // recommended for development ``` #### Rediriger les messages du système de journalisation \{#redirect-the-logging-system-messages\} Si vous avez besoin d'envoyer les messages de log d'Adapty vers votre propre système ou de les sauvegarder dans un fichier, utilisez la méthode `setLogHandler` et implémentez votre logique de journalisation personnalisée à l'intérieur. Ce gestionnaire reçoit des enregistrements de log contenant le contenu du message et son niveau de sévérité. ```swift showLineNumbers title="Swift" Adapty.setLogHandler { record in writeToLocalFile("Adapty \(record.level): \(record.message)") } ``` ### Politiques de données \{#data-policies\} Adapty ne stocke pas les données personnelles de vos utilisateurs à moins que vous ne les envoyiez explicitement, mais vous pouvez mettre en place des politiques de sécurité des données supplémentaires pour vous conformer aux directives du store ou du pays concerné. #### Désactiver la collecte et le partage de l'IDFA \{#disable-idfa-collection-and-sharing\} Lors de l'activation du module Adapty, définissez `idfaCollectionDisabled` sur `true` pour désactiver la collecte et le partage de l'IDFA. Utilisez ce paramètre pour respecter les directives de l'App Store Review Guidelines ou éviter de déclencher l'invite App Tracking Transparency lorsque l'IDFA n'est pas nécessaire pour votre application. La valeur par défaut est `false`. Pour plus d'informations sur la collecte de l'IDFA, consultez la section [Intégration Analytics](analytics-integration#disable-collection-of-advertising-identifiers). ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(idfaCollectionDisabled: true) ``` #### Désactiver la collecte et le partage de l'adresse IP \{#disable-ip-collection-and-sharing\} Lors de l'activation du module Adapty, définissez `ipAddressCollectionDisabled` sur `true` pour désactiver la collecte et le partage de l'adresse IP de l'utilisateur. La valeur par défaut est `false`. Utilisez ce paramètre pour renforcer la confidentialité des utilisateurs, respecter les réglementations régionales de protection des données (comme le RGPD ou le CCPA), ou réduire la collecte de données inutiles lorsque les fonctionnalités basées sur l'IP ne sont pas nécessaires pour votre application. ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(ipAddressCollectionDisabled: true) ``` #### Configuration du cache multimédia pour les paywalls dans AdaptyUI \{#media-cache-configuration-for-paywalls-in-adaptyui\} Notez que la configuration d'AdaptyUI est optionnelle. Vous pouvez activer le module AdaptyUI sans configuration. Cependant, si vous utilisez la configuration, tous les paramètres sont obligatoires. ```swift showLineNumbers title="Swift" // Configure AdaptyUI let adaptyUIConfiguration = AdaptyUI.Configuration( mediaCacheConfiguration: .init( memoryStorageTotalCostLimit: 100 * 1024 * 1024, memoryStorageCountLimit: .max, diskStorageSizeLimit: 100 * 1024 * 1024 ) ) // Activate AdaptyUI AdaptyUI.activate(configuration: adaptyUIConfiguration) ``` Paramètres : | Paramètre | Présence | Description | | :-------------------------- | :------- | :----------------------------------------------------------- | | memoryStorageTotalCostLimit | requis | Limite de coût total du stockage en octets. | | memoryStorageCountLimit | requis | Limite du nombre d'éléments dans le stockage en mémoire. | | diskStorageSizeLimit | requis | Limite de taille de fichier sur le disque en octets. 0 signifie aucune limite. | ### Comportement de finalisation des transactions \{#transaction-finishing-behavior\} :::info Cette fonctionnalité est disponible à partir de la version 3.12.0 du SDK. ::: Par défaut, Adapty finalise automatiquement les transactions après leur validation. Cependant, si vous avez besoin d'une validation avancée (validation côté serveur, détection de fraude ou logique métier personnalisée), vous pouvez configurer le SDK pour utiliser la finalisation manuelle des transactions. ```swift showLineNumbers title="Swift" let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(transactionsFinishBehavior: .manual) // .auto is the default ``` Pour plus de détails sur la façon de finaliser les transactions, consultez le [guide](ios-transaction-management). ### Effacer les données lors d'une restauration de sauvegarde \{#clear-data-on-backup-restore\} Lorsque `clearDataOnBackup` est défini sur `true`, le SDK détecte quand l'application est restaurée depuis une sauvegarde iCloud et supprime toutes les données SDK stockées localement, y compris les informations de profil en cache, les détails des produits et les paywalls. Le SDK s'initialise ensuite dans un état propre. La valeur par défaut est `false`. :::note Seul le cache local du SDK est supprimé. L'historique des transactions avec Apple et les données utilisateur sur les serveurs Adapty restent inchangés. ::: ```swift showLineNumbers let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") .with(clearDataOnBackup: true) // default – false ``` ## Dépannage \{#troubleshooting\} #### Erreur de concurrence Swift 6 avec Tuist \{#swift-6-concurrency-error-with-tuist\} Lors d'une compilation avec [Tuist](https://tuist.dev/), vous pouvez rencontrer des erreurs de compilation liées à la concurrence stricte de Swift 6. Les symptômes typiques incluent des incompatibilités d'attribut `@Sendable` dans `AdaptyUIBuilderLogic` ou des erreurs de Sendability similaires entre modules. Cela se produit parce que Tuist génère des projets Xcode à partir de packages SPM mais ne conserve pas le paramètre `swift-tools-version: 6.0`. En conséquence, certaines cibles Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`) compilent avec les règles Swift 5 tandis que d'autres utilisent Swift 6, ce qui crée des incompatibilités `@Sendable` entre modules. **Correction** : Mettez à niveau vers Adapty SDK **3.15.5** ou une version ultérieure, ce qui résout le problème indépendamment des versions mixtes du langage Swift. **Solution de contournement** : Si vous ne pouvez pas effectuer la mise à niveau, définissez explicitement Swift 6 pour les trois cibles Adapty dans votre configuration Tuist : ```swift showLineNumbers targetSettings: [ "Adapty": .init().swiftVersion("6"), "AdaptyUI": .init().swiftVersion("6"), "AdaptyUIBuilder": .init().swiftVersion("6"), ] ``` --- # File: ios-quickstart-paywalls --- --- title: "Activer les achats avec Flow Builder dans le SDK iOS" description: "Guide de démarrage rapide pour activer les achats intégrés avec Adapty Flow Builder." --- Pour activer les achats intégrés, vous devez comprendre trois concepts clés : - [**Produits**](product) – tout ce que les utilisateurs peuvent acheter (abonnements, consommables, accès à vie) - [**Flows**](adapty-flow-builder) – séquences d'écrans qui présentent des produits aux utilisateurs, créées dans le Flow Builder sans code. Le SDK les récupère via `getFlow`. Si vous préférez construire l'interface dans votre propre code, utilisez plutôt un paywall — voir [Implémenter des paywalls manuellement](ios-quickstart-manual). - [**Placements**](placements) – où et quand afficher les flows dans votre app (par exemple `main`, `onboarding`, `settings`). Vous associez des flows aux placements dans le tableau de bord, puis vous les demandez par ID de placement dans votre code. Cela facilite l'exécution de tests A/B et l'affichage de flows différents selon les utilisateurs. Adapty vous propose trois façons d'activer les achats dans votre app. Choisissez celle qui correspond à vos besoins : | Implémentation | Complexité | Quand l'utiliser | |---|---|---| | Adapty Flow Builder | ✅ Facile | Vous [créez un flow complet et prêt à l'achat dans le builder sans code](quickstart-paywalls). Adapty le rend automatiquement et gère l'intégralité du flux d'achat, la validation des reçus et la gestion des abonnements en coulisses. | | Paywalls créés manuellement | 🟡 Moyen | Vous implémentez l'interface de votre paywall dans le code de votre app, mais récupérez quand même l'objet flow depuis Adapty pour conserver de la flexibilité dans les offres de produits. Voir le [guide](ios-quickstart-manual). | | Mode observateur | 🔴 Difficile | Vous disposez déjà de votre propre infrastructure de gestion des achats et souhaitez continuer à l'utiliser. Notez que le mode observateur a ses limitations dans Adapty. Voir l'[article](observer-vs-full-mode). | :::important **Les étapes ci-dessous montrent comment implémenter un flow créé dans Adapty Flow Builder.** Si vous préférez construire l'interface du paywall vous-même, voir [Implémenter des paywalls manuellement](ios-quickstart-manual). ::: Pour afficher un flow créé dans Adapty Flow Builder, vous n'avez besoin que de : 1. **Récupérer le flow** : Obtenez-le depuis Adapty. 2. **L'afficher et laisser Adapty gérer les achats** : Affichez la vue dans votre app. 3. **Gérer les actions des boutons** : Associez les interactions utilisateur aux réponses de votre app. Par exemple, ouvrez des liens ou fermez le flow lorsque les utilisateurs cliquent sur des boutons. ## Avant de commencer \{#before-you-start\} Avant de commencer, effectuez ces étapes : 1. [Connectez votre app à l'App Store](initial_ios) dans l'Adapty Dashboard. 2. [Créez vos produits](create-product) dans Adapty. 3. [Créez un flow et ajoutez-y des produits](create-paywall). 4. [Créez un placement et ajoutez votre flow](create-placement). 5. [Installez et activez le SDK Adapty](sdk-installation-ios) dans votre code. Ce guide utilise les APIs du SDK Adapty iOS v4. ## 1. Récupérer le flow \{#1-get-the-flow\} Vos flows sont associés à des placements configurés dans le tableau de bord. Les placements vous permettent d'exécuter des flows différents pour différentes audiences ou de lancer des [tests A/B](ab-tests). Pour obtenir un flow créé dans Adapty Flow Builder, vous devez : 1. Récupérer l'objet `flow` par l'ID du [placement](placements) via la méthode `getFlow` et vérifier qu'il dispose d'une configuration de vue. 2. Obtenir la configuration de vue via la méthode `getFlowConfiguration`. Elle contient les éléments d'interface et le style nécessaires pour afficher le flow. ```swift func loadFlow() async { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") guard flow.hasViewConfiguration else { print("Flow doesn't have a view configuration") return } flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow) } ``` ## 2. Afficher le flow \{#2-display-the-flow\} Maintenant que vous avez la configuration du flow, quelques lignes suffisent pour l'afficher. En SwiftUI, lors de l'affichage du flow, vous devez également gérer les événements. `didFinishPurchase`, `didFailPurchase`, `didFinishRestore`, `didFailRestore` et `didReceiveError` sont obligatoires. Pour les tests, vous pouvez simplement copier le code du snippet ci-dessous pour journaliser ces événements. :::tip Le flow ne se ferme pas automatiquement après un achat réussi. Dans `didFinishPurchase`, passez votre binding de présentation à `false` pour le fermer, ou ne faites rien pour laisser le flow continuer vers les écrans suivants. ::: ```swift .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didFinishPurchase: { product, purchaseResult in print("Purchase finished successfully") flowPresented = false // or do nothing to let the flow continue }, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didReceiveError: { error in flowPresented = false print("Flow error: \(error)") } ) ``` ```swift func presentFlow(with config: AdaptyUI.FlowConfiguration) { let flowController = try AdaptyUI.flowController( with: config, delegate: self ) present(flowController, animated: true) } ``` Implémentez `AdaptyFlowControllerDelegate` pour gérer les événements. Au minimum, implémentez les quatre méthodes sans implémentation par défaut. Notez que le contrôleur ne se ferme pas tout seul après un achat réussi — fermez-le dans `didFinishPurchase`, ou ne faites rien pour laisser le flow continuer vers les écrans suivants : ```swift extension YourViewController: AdaptyFlowControllerDelegate { func flowController(_ controller: AdaptyFlowController, didFinishPurchase product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult) { if !purchaseResult.isPurchaseCancelled { controller.dismiss(animated: true) // or do nothing to let the flow continue } } func flowController(_ controller: AdaptyFlowController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError) { print("Purchase failed: \(error)") } func flowController(_ controller: AdaptyFlowController, didFinishRestoreWith profile: AdaptyProfile) { print("Restore finished successfully") } func flowController(_ controller: AdaptyFlowController, didFailRestoreWith error: AdaptyError) { print("Restore failed: \(error)") } } ``` :::info Pour plus de détails sur l'affichage d'un flow, consultez notre [guide](ios-present-paywalls). ::: ## 3. Gérer les actions des boutons \{#3-handle-button-actions\} Lorsque les utilisateurs cliquent sur des boutons, le SDK iOS gère automatiquement les achats, la restauration, la fermeture du flow et l'ouverture des liens. Cependant, d'autres boutons ont des identifiants personnalisés ou prédéfinis et nécessitent une gestion dans votre code. Vous pouvez également vouloir remplacer leur comportement par défaut. Par exemple, voici comment gérer le bouton de fermeture. En UIKit, le SDK ferme le contrôleur automatiquement lorsque `.close` se déclenche — ne surchargez que si vous souhaitez un comportement personnalisé. En SwiftUI, vous devez passer votre binding `isPresented` à `false` vous-même. :::tip Consultez nos guides sur la gestion des [actions](handle-paywall-actions) et des [événements](ios-handling-events) des boutons. ::: ```swift .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false // dismiss the flow when the user taps close default: break } }, didFinishPurchase: { product, purchaseResult in flowPresented = false }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) ``` ```swift extension YourViewController: AdaptyFlowControllerDelegate { func flowController(_ controller: AdaptyFlowController, didPerform action: AdaptyUI.Action) { switch action { case .close: controller.dismiss(animated: true) // default behavior — override only if needed default: break } } } ``` ## Étapes suivantes \{#next-steps\} :::tip Des questions ou des problèmes ? Consultez notre [forum d'assistance](https://adapty.featurebase.app/) où vous trouverez des réponses aux questions fréquentes ou pourrez poser les vôtres. Notre équipe et notre communauté sont là pour vous aider ! ::: Votre flow est prêt à être affiché dans l'app. [Testez vos achats en mode sandbox](test-purchases-in-sandbox) pour vous assurer de pouvoir effectuer un achat de test. Vous devez ensuite [vérifier le niveau d'accès des utilisateurs](ios-check-subscription-status) pour vous assurer d'afficher un flow ou de donner accès aux fonctionnalités payantes aux bons utilisateurs. ## Exemple complet \{#full-example\} Voici comment toutes les étapes de ce guide peuvent être intégrées ensemble dans votre app. ```swift struct ContentView: View { @State private var flowPresented = false @State private var flowConfiguration: AdaptyUI.FlowConfiguration? @State private var isLoading = false @State private var hasInitialized = false var body: some View { VStack { if isLoading { ProgressView("Loading...") } else { Text("Your App Content") } } .task { guard !hasInitialized else { return } await initializeFlow() hasInitialized = true } .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false default: break } }, didFinishPurchase: { product, purchaseResult in print("Purchase finished successfully") flowPresented = false // or do nothing to let the flow continue }, didFailPurchase: { product, error in print("Purchase failed: \(error)") }, didFinishRestore: { profile in print("Restore finished successfully") }, didFailRestore: { error in print("Restore failed: \(error)") }, didReceiveError: { error in print("Flow error: \(error)") flowPresented = false } ) } private func initializeFlow() async { isLoading = true defer { isLoading = false } await loadFlow() flowPresented = true } private func loadFlow() async { do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") guard flow.hasViewConfiguration else { print("Flow doesn't have a view configuration") return } flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow) } catch { print("Failed to load: \(error)") } } } ``` ```swift class ViewController: UIViewController { private var flowConfiguration: AdaptyUI.FlowConfiguration? override func viewDidLoad() { super.viewDidLoad() Task { await initializeFlow() } } private func initializeFlow() async { do { flowConfiguration = try await loadFlow() if let flowConfiguration { await MainActor.run { presentFlow(with: flowConfiguration) } } } catch { print("Error initializing: \(error)") } } private func loadFlow() async throws -> AdaptyUI.FlowConfiguration? { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") guard flow.hasViewConfiguration else { print("Flow doesn't have a view configuration") return nil } return try await AdaptyUI.getFlowConfiguration(forFlow: flow) } private func presentFlow(with config: AdaptyUI.FlowConfiguration) { guard let flowController = try? AdaptyUI.flowController( with: config, delegate: self ) else { return } present(flowController, animated: true) } } extension ViewController: AdaptyFlowControllerDelegate { func flowController(_ controller: AdaptyFlowController, didFinishPurchase product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult) { if !purchaseResult.isPurchaseCancelled { controller.dismiss(animated: true) // or do nothing to let the flow continue } } func flowController(_ controller: AdaptyFlowController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError) { print("Purchase failed for \(product.vendorProductId): \(error)") guard error.adaptyErrorCode != .paymentCancelled else { return } let message = switch error.adaptyErrorCode { case .paymentNotAllowed: "Purchases are not allowed on this device." default: "Purchase failed. Please try again." } let alert = UIAlertController(title: "Purchase Error", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) } func flowController(_ controller: AdaptyFlowController, didFinishRestoreWith profile: AdaptyProfile) { print("Restore finished successfully") controller.dismiss(animated: true) } func flowController(_ controller: AdaptyFlowController, didFailRestoreWith error: AdaptyError) { print("Restore failed: \(error)") } func flowController(_ controller: AdaptyFlowController, didReceiveError error: AdaptyUIError) { print("Flow error: \(error)") controller.dismiss(animated: true) } } ``` --- # File: ios-check-subscription-status --- --- title: "Vérifier le statut d'abonnement dans le SDK iOS" description: "Découvrez comment vérifier le statut d'abonnement dans votre application iOS avec Adapty." --- Pour décider si les utilisateurs peuvent accéder au contenu payant ou voir un paywall, vous devez vérifier leur [niveau d'accès](access-level) dans le profil. Cet article vous montre comment accéder à l'état du profil pour décider ce que les utilisateurs doivent voir — s'il faut leur afficher un paywall ou leur accorder l'accès aux fonctionnalités payantes. ## Obtenir le statut d'abonnement \{#get-subscription-status\} Lorsque vous décidez d'afficher un paywall ou du contenu payant à un utilisateur, vous vérifiez son [niveau d'accès](access-level) dans son profil. Deux options s'offrent à vous : - Appelez `getProfile` si vous avez besoin des données de profil les plus récentes immédiatement (par exemple au lancement de l'application) ou si vous souhaitez forcer une mise à jour. - Configurez des **mises à jour automatiques du profil** pour conserver une copie locale qui se rafraîchit automatiquement dès que le statut d'abonnement change. :::important Par défaut, le niveau d'accès `premium` existe déjà dans Adapty. Si vous n'avez pas besoin de configurer plus d'un niveau d'accès, vous pouvez simplement utiliser `premium`. ::: ### Récupérer le profil \{#get-profile\} La façon la plus simple d'obtenir le statut d'abonnement est d'utiliser la méthode `getProfile` pour accéder au profil : ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` ### Écouter les mises à jour d'abonnement \{#listen-to-subscription-updates\} Si vous souhaitez recevoir automatiquement les mises à jour du profil dans votre application : 1. Conformez un type de votre choix au protocole `AdaptyDelegate` et implémentez la méthode `didLoadLatestProfile` — Adapty appellera automatiquement cette méthode dès que le statut d'abonnement de l'utilisateur change. Dans l'exemple ci-dessous, nous utilisons un type `SubscriptionManager` pour gérer les flux d'abonnement et le profil de l'utilisateur. Ce type peut être injecté en tant que dépendance ou configuré comme singleton dans une application UIKit, ou ajouté à l'environnement SwiftUI depuis la structure principale de l'application. 2. Stockez les données de profil mises à jour lorsque cette méthode est appelée, afin de pouvoir les utiliser dans toute votre application sans effectuer de requêtes réseau supplémentaires. ```swift class SubscriptionManager: AdaptyDelegate { nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { let hasAccess = profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false // Update UI, unlock content, etc. } } // Set delegate after Adapty activation Adapty.delegate = subscriptionManager ``` :::note Adapty appelle automatiquement `didLoadLatestProfile` au démarrage de votre application, fournissant les données d'abonnement en cache même si l'appareil est hors ligne. ::: ## Connecter le profil à la logique des paywalls \{#connect-profile-with-paywall-logic\} Lorsque vous devez prendre des décisions immédiates concernant l'affichage de paywalls ou l'accès aux fonctionnalités payantes, vous pouvez vérifier directement le profil de l'utilisateur. Cette approche est utile dans des scénarios comme le lancement de l'application, l'accès à des sections premium, ou avant d'afficher un contenu spécifique. ```swift private func checkAccessLevel() async -> Bool { do { let profile = try await Adapty.getProfile() return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false } catch { print("Error checking access level: \(error)") return false } } // In your initialization logic: let hasAccess = await checkAccessLevel() if !hasAccess { paywallPresented = true // Show paywall if no access } ``` ```swift private func checkAccessLevel() async throws -> Bool { let profile = try await Adapty.getProfile() return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false } // In your initialization logic: let hasAccess = try await checkAccessLevel() if !hasAccess { presentPaywall(with: paywallConfiguration) } ``` ## Étapes suivantes \{#next-steps\} Maintenant que vous savez comment suivre le statut d'abonnement, [découvrez comment travailler avec les profils utilisateurs](ios-quickstart-identify) pour vous assurer qu'il s'aligne avec votre système d'authentification existant et les autorisations de partage d'accès payant. Si vous n'avez pas votre propre système d'authentification, ce n'est pas un problème, Adapty gérera les utilisateurs pour vous, mais vous pouvez quand même lire le [guide](ios-quickstart-identify) pour comprendre comment Adapty fonctionne avec les utilisateurs anonymes. --- # File: ios-quickstart-identify --- --- title: "Identifier les utilisateurs dans le SDK iOS" description: "Guide de démarrage rapide pour configurer Adapty pour la gestion des abonnements intégrés." --- :::important Ce guide vous concerne si vous disposez de votre propre système d'authentification. Vous y apprendrez comment travailler avec les profils utilisateur dans Adapty afin de les aligner avec votre système d'authentification existant. ::: La façon dont vous gérez les achats des utilisateurs dépend du modèle d'authentification de votre application : - Si votre application n'utilise pas d'authentification backend et ne stocke pas de données utilisateur, consultez la [section sur les utilisateurs anonymes](#anonymous-users). - Si votre application dispose (ou disposera) d'une authentification backend, consultez la [section sur les utilisateurs identifiés](#identified-users). **Concepts clés** : - Les **profils** sont les entités nécessaires au fonctionnement du SDK. Adapty les crée automatiquement. - Ils peuvent être anonymes **(sans customer user ID)** ou identifiés **(avec customer user ID)**. - Vous fournissez le **customer user ID** pour faire le lien entre les profils Adapty et votre système d'authentification interne. Voici les différences entre utilisateurs anonymes et identifiés : | | Utilisateurs anonymes | Utilisateurs identifiés | |------------------------------|--------------------------------------------------------------|--------------------------------------------------------------------------------------| | **Gestion des achats** | Restauration des achats au niveau du store | Historique des achats conservé sur tous les appareils via leur customer user ID | | **Gestion des profils** | Nouveau profil à chaque réinstallation | Le même profil sur toutes les sessions et tous les appareils | | **Persistance des données** | Les données des utilisateurs anonymes sont liées à l'installation de l'app | Les données des utilisateurs identifiés persistent entre les installations | ## Utilisateurs anonymes \{#anonymous-users\} Si vous n'avez pas d'authentification backend, **vous n'avez pas besoin de gérer l'authentification dans le code de l'application** : 1. Lors de l'activation du SDK au premier lancement de l'app, Adapty **crée un nouveau profil pour l'utilisateur**. 2. Lorsque l'utilisateur effectue un achat dans l'app, cet achat est **associé à son profil Adapty et à son compte store**. 3. Lorsque l'utilisateur **réinstalle** l'app ou l'installe sur un **nouvel appareil**, Adapty **crée un nouveau profil anonyme lors de l'activation**. 4. Si l'utilisateur a déjà effectué des achats dans votre application, ses achats sont automatiquement synchronisés depuis l'App Store lors de l'activation du SDK. :::note Les restaurations depuis une sauvegarde se comportent différemment des réinstallations. Par défaut, lorsqu'un utilisateur restaure depuis une sauvegarde, le SDK conserve les données en cache et ne crée pas de nouveau profil. Vous pouvez configurer ce comportement avec le paramètre `clearDataOnBackup`. [En savoir plus](sdk-installation-ios#clear-data-on-backup-restore). ::: Ainsi, avec des utilisateurs anonymes, de nouveaux profils seront créés à chaque installation, mais ce n'est pas un problème car, dans les analyses Adapty, vous pouvez [configurer ce qui sera considéré comme une nouvelle installation](general#4-installs-definition-for-analytics). Pour les utilisateurs anonymes, vous devez compter les installations par **ID d'appareil**. Dans ce cas, chaque installation de l'application sur un appareil est comptée comme une installation, y compris les réinstallations. ## Utilisateurs identifiés \{#identified-users\} Vous avez deux options pour identifier les utilisateurs dans l'application : - [**Lors de la connexion/inscription :**](#during-loginsignup) Si les utilisateurs se connectent après le démarrage de votre application, appelez `identify()` avec un customer user ID lorsqu'ils s'authentifient. - [**Lors de l'activation du SDK :**](#during-the-sdk-activation) Si vous disposez déjà d'un customer user ID stocké au lancement de l'application, envoyez-le lors de l'appel à `activate()`. :::important Par défaut, lorsqu'Adapty reçoit un achat d'un Customer User ID actuellement associé à un autre Customer User ID, le niveau d'accès est partagé, de sorte que les deux profils ont un accès payant. Vous pouvez configurer ce paramètre pour transférer l'accès payant d'un profil à un autre ou désactiver complètement le partage. Consultez l'[article](general#6-sharing-paid-access-between-user-accounts) pour plus de détails. ::: ### Lors de la connexion/inscription \{#during-loginsignup\} Si vous identifiez les utilisateurs après le lancement de l'application (par exemple, après qu'ils se soient connectés ou inscrits), utilisez la méthode `identify` pour définir leur customer user ID. - Si vous **n'avez pas encore utilisé ce customer user ID**, Adapty le liera automatiquement au profil actuel. - Si vous **avez déjà utilisé ce customer user ID pour identifier l'utilisateur**, Adapty basculera vers le profil associé à ce customer user ID. :::important Les customer user IDs doivent être uniques pour chaque utilisateur. Si vous codez en dur la valeur du paramètre, tous les utilisateurs seront considérés comme un seul. ::: Attendez toujours que `identify` soit résolu (`await`) avant d'appeler d'autres méthodes du SDK. Les appels simultanés produisent l'erreur `#3006 profileWasChanged` ou atterrissent sur le profil anonyme. Voir [Ordre des appels dans le SDK iOS](ios-sdk-call-order). ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") // Unique for each user } catch { // handle the error } ``` ```swift showLineNumbers // User IDs must be unique for each user Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` ### Lors de l'activation du SDK \{#during-the-sdk-activation\} Si vous connaissez déjà un customer user ID au moment d'activer le SDK, vous pouvez l'inclure dans la méthode `activate` plutôt que d'appeler `identify` séparément. Si vous connaissez un customer user ID mais ne le définissez qu'après l'activation, cela signifie qu'à l'activation, Adapty créera un nouveau profil anonyme et ne basculera vers le profil existant qu'après votre appel à `identify`. Vous pouvez passer un customer user ID existant (que vous avez déjà utilisé) ou un nouveau. Si vous en passez un nouveau, le profil créé lors de l'activation sera automatiquement lié à ce customer user ID. :::note Par défaut, la création de profils anonymes n'affecte pas les tableaux de bord d'analyse, car les installations sont comptées en fonction des ID d'appareils. Un ID d'appareil représente une seule installation de l'application depuis le store sur un appareil et n'est régénéré qu'après la réinstallation de l'app. Il ne dépend pas du fait qu'il s'agisse d'une première ou d'une nouvelle installation, ni de l'utilisation d'un customer user ID existant. La création d'un profil (lors de l'activation du SDK ou de la déconnexion), la connexion ou la mise à jour de l'app sans réinstallation ne génère pas d'événements d'installation supplémentaires. Si vous souhaitez compter les installations en fonction des utilisateurs uniques plutôt que des appareils, accédez à **App settings** et configurez [**Installs definition for analytics**](general#4-installs-definition-for-analytics). ::: ```swift showLineNumbers // Place in the app main struct for SwiftUI or in AppDelegate for UIKit let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } ``` ```swift showLineNumbers // Place in the app main struct for SwiftUI or in AppDelegate for UIKit let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one. Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` ### Déconnecter les utilisateurs \{#log-users-out\} Si votre application dispose d'un bouton de déconnexion, utilisez la méthode `logout`. :::important La déconnexion d'un utilisateur crée un nouveau profil anonyme pour cet utilisateur. ::: ```swift showLineNumbers do { try await Adapty.logout() } catch { // handle the error } ``` ```swift showLineNumbers Adapty.logout { error in if error == nil { // successful logout } } ``` :::info Pour reconnecter les utilisateurs à l'application, utilisez la méthode `identify`. ::: ### Autoriser les achats sans connexion \{#allow-purchases-without-login\} Si vos utilisateurs peuvent effectuer des achats avant et après leur connexion à votre application, vous devez vous assurer qu'ils conserveront leur accès après la connexion : 1. Lorsqu'un utilisateur non connecté effectue un achat, Adapty le lie à son ID de profil anonyme. 2. Lorsque l'utilisateur se connecte à son compte, Adapty bascule vers son profil identifié. - S'il s'agit d'un nouveau customer user ID (par exemple, l'achat a été effectué avant l'inscription), Adapty attribue le customer user ID au profil actuel, de sorte que tout l'historique des achats est conservé. - S'il s'agit d'un customer user ID existant (déjà lié à un profil), vous devez récupérer le niveau d'accès réel après le changement de profil. Vous pouvez soit appeler [`getProfile`](ios-check-subscription-status) juste après l'identification, soit [écouter les mises à jour du profil](ios-check-subscription-status) pour que les données se synchronisent automatiquement. ## Prochaines étapes \{#next-steps\} Félicitations ! Vous avez implémenté la logique de paiement intégré dans votre application ! Nous vous souhaitons tout le succès possible pour la monétisation de votre app ! Pour tirer encore plus parti d'Adapty, vous pouvez explorer ces sujets : - [**Tests**](test-purchases-in-sandbox) : Vérifiez que tout fonctionne comme prévu - [**Onboardings**](ios-onboardings) : Engagez vos utilisateurs avec des onboardings et favorisez la rétention - [**Intégrations**](configuration) : Intégrez des services d'attribution marketing et d'analyse en une seule ligne de code - [**Définir des attributs de profil personnalisés**](setting-user-attributes) : Ajoutez des attributs personnalisés aux profils utilisateur et créez des segments pour lancer des tests A/B ou afficher des paywalls différents selon les utilisateurs --- # File: adapty-sdk-integration-skill --- --- title: "Intégrer Adapty dans votre application iOS avec la compétence d'intégration SDK" description: "Utilisez la compétence adapty-sdk-integration pour intégrer le SDK Adapty dans votre application iOS de bout en bout avec votre outil de codage IA." --- :::important La compétence est en bêta. Si elle se bloque ou se comporte de manière inattendue, suivez le [guide d'intégration étape par étape](adapty-cursor) à la place — il guide votre outil IA à travers chaque étape avec la bonne documentation. ::: La [compétence adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatise l'intégration Adapty de bout en bout : configuration du tableau de bord, installation du SDK, paywall et vérification à chaque étape. Elle détecte automatiquement votre plateforme et récupère la documentation Adapty pertinente à chaque étape. **Outils compatibles** : Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI. Pour installer, choisissez le formulaire correspondant à votre outil. La liste complète se trouve dans le [README de la compétence](https://github.com/adaptyteam/adapty-sdk-integration-skill). **Claude Code** ``` claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill claude plugin install adapty-sdk-integration@adapty ``` **GitHub Copilot CLI** ``` gh skill install adaptyteam/adapty-sdk-integration-skill ``` **Gemini CLI** ``` gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill ``` **OpenAI Codex ou tout autre outil** — utilisez la [CLI skills](https://skills.sh) (notez que les compétences installées de cette façon ne se mettent pas à jour automatiquement) : ``` npx skills add adaptyteam/adapty-sdk-integration-skill ``` Vous pouvez également cloner le dépôt et copier `skills/adapty-sdk-integration/` dans le répertoire des compétences de votre outil. Après l'installation, exécutez la compétence dans votre projet : ``` /adapty-sdk-integration ``` La compétence pose quelques questions de configuration, puis guide à travers la configuration du tableau de bord, l'installation du SDK, le paywall et la vérification. --- # File: adapty-cursor --- --- title: "Intégrer Adapty dans votre app iOS avec l'aide de l'IA" description: "Un guide étape par étape pour intégrer Adapty dans votre app iOS avec Cursor, Context7, ChatGPT, Claude ou d'autres outils IA." --- Ce guide vous accompagne pas à pas dans l'intégration d'Adapty dans votre app iOS avec un outil de codage IA — vous lui fournissez la bonne documentation Adapty dans le bon ordre. For a fully automated integration, use the [adapty-sdk-integration skill](https://github.com/adaptyteam/adapty-sdk-integration-skill): it runs the whole integration from your AI coding tool in one command. ## Avant de commencer : configuration du tableau de bord \{#before-you-start-dashboard-setup\} Adapty nécessite une configuration dans le tableau de bord avant d'écrire le moindre code SDK. Vous pouvez le faire avec un skill LLM interactif, ou manuellement via le Dashboard. ### Approche par skill (recommandée) \{#skill-approach-recommended\} Le skill Adapty CLI permet à votre LLM de configurer votre app, vos produits, niveaux d'accès, paywalls et placements directement — sans avoir à ouvrir le Dashboard à chaque étape. Vous devez uniquement [connecter votre store](integrate-payments) dans le Dashboard. ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` Une fois le skill ajouté, lancez `/adapty-cli` dans votre agent. Il vous guidera à chaque étape — y compris pour savoir quand ouvrir le Dashboard afin de connecter votre store. ### Approche manuelle \{#dashboard-approach\} Si vous préférez tout configurer manuellement, voici ce dont vous avez besoin avant d'écrire du code. Votre LLM ne peut pas récupérer les valeurs du tableau de bord à votre place — vous devrez les lui fournir. 1. **Connectez votre app store** : Dans l'Adapty Dashboard, allez dans **App settings → General**. C'est indispensable pour que les achats fonctionnent. [Connecter l'App Store](integrate-payments) 2. **Copiez votre clé SDK publique** : Dans l'Adapty Dashboard, allez dans **App settings → General**, puis trouvez la section **API keys**. Dans le code, c'est la chaîne que vous passez à `Adapty.activate("YOUR_PUBLIC_SDK_KEY")`. 3. **Créez au moins un produit** : Dans l'Adapty Dashboard, rendez-vous sur la page **Products**. Vous ne référencez pas les produits directement dans le code — Adapty les transmet via des flows ou des paywalls. [Ajouter des produits](quickstart-products) 4. **Créez un flow ou un paywall et un placement** : Dans l'Adapty Dashboard, créez un flow (ou un paywall si vous construisez l'interface vous-même), puis assignez-le à un placement sur la page **Placements**. Dans le code, l'ID de placement est la chaîne que vous passez à `Adapty.getFlow("YOUR_PLACEMENT_ID")`. [Créer un flow](quickstart-paywalls) 5. **Configurez les niveaux d'accès** : Dans l'Adapty Dashboard, configurez-les par produit sur la page **Products**. Dans le code, la chaîne vérifiée dans `profile.accessLevels["premium"]`. Le niveau d'accès `premium` par défaut convient à la plupart des apps. Si les utilisateurs payants ont accès à des fonctionnalités différentes selon le produit (par exemple, un plan `basic` vs. un plan `pro`), [créez des niveaux d'accès supplémentaires](assigning-access-level-to-a-product) avant de commencer à coder. :::tip Une fois ces cinq éléments en place, vous êtes prêt à écrire du code. Dites à votre LLM : « Ma clé SDK publique est X, mon ID de placement est Y » pour qu'il puisse générer le bon code d'initialisation et de récupération du paywall. ::: ### À configurer quand vous êtes prêt \{#set-up-when-ready\} Ces éléments ne sont pas indispensables pour commencer à coder, mais vous en aurez besoin au fur et à mesure que votre intégration évolue : - **Tests A/B** : Configurez-les sur la page **Placements**. Aucun changement de code requis. [Tests A/B](ab-tests) - **Flows et placements supplémentaires** : Ajoutez d'autres appels `getFlow` avec différents IDs de placement. - **Intégrations analytics** : Configurez-les sur la page **Integrations**. La procédure varie selon l'intégration. Voir [intégrations analytics](analytics-integration) et [intégrations attribution](attribution-integration). ## Fournir la documentation Adapty à votre LLM \{#feed-adapty-docs-to-your-llm\} ### Utiliser Context7 (recommandé) \{#use-context7-recommended\} [Context7](https://context7.com) est un serveur MCP qui donne à votre LLM un accès direct à la documentation Adapty à jour. Votre LLM récupère automatiquement les bons docs en fonction de vos questions — pas besoin de coller des URLs manuellement. Context7 fonctionne avec **Cursor**, **Claude Code**, **Windsurf** et d'autres outils compatibles MCP. Pour le configurer, lancez : ``` npx ctx7 setup ``` Cela détecte votre éditeur et configure le serveur Context7. Pour une configuration manuelle, consultez le [dépôt GitHub de Context7](https://github.com/upstash/context7). Une fois configuré, référencez la bibliothèque Adapty dans vos prompts : ``` Use the adaptyteam/adapty-docs library to look up how to install the iOS SDK ``` :::warning Même si Context7 évite de coller des liens manuellement, l'ordre d'implémentation est important. Suivez le [parcours d'implémentation](#implementation-walkthrough) ci-dessous étape par étape pour que tout fonctionne correctement. ::: ### Utiliser les docs en texte brut \{#use-plain-text-docs\} Vous pouvez accéder à n'importe quelle page de documentation Adapty en texte brut Markdown. Ajoutez `.md` à la fin de son URL, ou cliquez sur **Copy for LLM** sous le titre de l'article. Par exemple : [adapty-cursor.md](https://adapty.io/docs/fr/adapty-cursor.md). Chaque étape du [parcours d'implémentation](#implementation-walkthrough) ci-dessous inclut un bloc « À envoyer à votre LLM » avec des liens `.md` à coller. Pour accéder à plus de documentation en une fois, consultez les [fichiers index et sous-ensembles par plateforme](#plain-text-doc-index-files) ci-dessous. ## Parcours d'implémentation \{#implementation-walkthrough\} La suite de ce guide parcourt l'intégration d'Adapty dans l'ordre d'implémentation. Chaque étape inclut les docs à envoyer à votre LLM, ce que vous devriez voir une fois terminé, et les problèmes courants. ### Planifier votre intégration \{#plan-your-integration\} Avant de plonger dans le code, demandez à votre LLM d'analyser votre projet et de créer un plan d'implémentation. Si votre outil IA dispose d'un mode de planification (comme le mode plan de Cursor ou de Claude Code), utilisez-le pour que le LLM puisse lire à la fois la structure de votre projet et la documentation Adapty avant d'écrire du code. Indiquez à votre LLM quelle approche vous utilisez pour les achats — cela détermine les guides à suivre : - [**Adapty Flow Builder**](adapty-flow-builder) : Vous créez des flows dans le builder no-code d'Adapty, et le SDK les affiche automatiquement. - [**Paywalls créés manuellement**](ios-quickstart-manual) : Vous construisez votre propre interface de paywall dans le code, mais utilisez quand même Adapty pour récupérer les produits et gérer les achats. - [**Mode Observer**](observer-vs-full-mode) : Vous conservez votre infrastructure d'achats existante et utilisez Adapty uniquement pour les analytics et les intégrations. Vous ne savez pas lequel choisir ? Lisez le [tableau comparatif dans le quickstart](ios-quickstart-paywalls). ### Installer et configurer le SDK \{#install-and-configure-the-sdk\} Installez le package SDK Adapty via Swift Package Manager dans Xcode et activez-le avec votre clé SDK publique. C'est la base — rien d'autre ne fonctionnera sans ça. **Guide :** [Installer et configurer le SDK Adapty](sdk-installation-ios) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/sdk-installation-ios.md ``` :::tip[Checkpoint] - **Attendu :** L'app se build et se lance. La console Xcode affiche le log d'activation d'Adapty. - **Piège :** « Public API key is missing » → vérifiez que vous avez remplacé le placeholder par votre vraie clé depuis App settings. ::: ### Afficher les flows ou paywalls et gérer les achats \{#show-flows-or-paywalls-and-handle-purchases\} Récupérez un flow ou un paywall par son ID de placement, affichez-le et gérez les événements d'achat. Les guides nécessaires dépendent de votre approche pour les achats. Testez chaque achat en sandbox au fur et à mesure — n'attendez pas la fin. Consultez [Tester les achats en sandbox](test-purchases-in-sandbox) pour les instructions de configuration. **Guides :** - [Activer les achats avec Flow Builder (quickstart)](ios-quickstart-paywalls) - [Récupérer les flows et leur configuration](get-pb-paywalls) - [Afficher les flows](ios-present-paywalls) - [Gérer les événements de flow](ios-handling-events) - [Répondre aux actions des boutons](handle-paywall-actions) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/ios-quickstart-paywalls.md - https://adapty.io/docs/fr/get-pb-paywalls.md - https://adapty.io/docs/fr/ios-present-paywalls.md - https://adapty.io/docs/fr/ios-handling-events.md - https://adapty.io/docs/fr/handle-paywall-actions.md ``` :::tip[Checkpoint] - **Attendu :** Le flow s'affiche avec vos produits configurés. Appuyer sur un produit déclenche la boîte de dialogue d'achat sandbox. - **Piège :** Flow vide ou erreur `getFlow` → vérifiez que l'ID de placement correspond exactement à celui du tableau de bord et que le placement a une audience assignée. ::: **Guides :** - [Activer les achats dans votre paywall personnalisé (quickstart)](ios-quickstart-manual) - [Récupérer les paywalls et les produits](fetch-paywalls-and-products) - [Afficher un paywall conçu avec Remote Config](present-remote-config-paywalls) - [Effectuer des achats](making-purchases) - [Restaurer les achats](restore-purchase) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/ios-quickstart-manual.md - https://adapty.io/docs/fr/fetch-paywalls-and-products.md - https://adapty.io/docs/fr/present-remote-config-paywalls.md - https://adapty.io/docs/fr/making-purchases.md - https://adapty.io/docs/fr/restore-purchase.md ``` :::tip[Checkpoint] - **Attendu :** Votre paywall personnalisé affiche les produits récupérés depuis Adapty. Appuyer sur un produit déclenche la boîte de dialogue d'achat sandbox. - **Piège :** Tableau de produits vide → vérifiez que le paywall a des produits assignés dans le tableau de bord et que le placement a une audience. ::: **Guides :** - [Vue d'ensemble du mode Observer](observer-vs-full-mode) - [Implémenter le mode Observer](implement-observer-mode) - [Signaler les transactions en mode Observer](report-transactions-observer-mode) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/observer-vs-full-mode.md - https://adapty.io/docs/fr/implement-observer-mode.md - https://adapty.io/docs/fr/report-transactions-observer-mode.md ``` :::tip[Checkpoint] - **Attendu :** Après un achat sandbox via votre flux d'achat existant, la transaction apparaît dans le **Event Feed** du tableau de bord Adapty. - **Piège :** Aucun événement → vérifiez que vous signalez bien les transactions à Adapty et que les App Store Server Notifications sont configurées. ::: ### Vérifier le statut de l'abonnement \{#check-subscription-status\} Après un achat, vérifiez le profil utilisateur pour détecter un niveau d'accès actif et restreindre le contenu premium. **Guide :** [Vérifier le statut de l'abonnement](ios-check-subscription-status) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/ios-check-subscription-status.md ``` :::tip[Checkpoint] - **Attendu :** Après un achat sandbox, `profile.accessLevels["premium"]?.isActive` retourne `true`. - **Piège :** `accessLevels` vide après un achat → vérifiez que le produit a un niveau d'accès assigné dans le tableau de bord. ::: ### Identifier les utilisateurs \{#identify-users\} Liez les comptes utilisateurs de votre app aux profils Adapty pour que les achats persistent sur tous les appareils. :::important Ignorez cette étape si votre app ne gère pas l'authentification. ::: **Guide :** [Identifier les utilisateurs](ios-quickstart-identify) À envoyer à votre LLM : ``` Read these Adapty docs before writing code: - https://adapty.io/docs/fr/ios-quickstart-identify.md ``` :::tip[Checkpoint] - **Attendu :** Après avoir appelé `Adapty.identify("your-user-id")`, la section **Profiles** du tableau de bord affiche votre ID utilisateur personnalisé. - **Piège :** Appelez `identify` après l'activation mais avant de récupérer les paywalls pour éviter une attribution au profil anonyme. ::: ### Préparer la mise en production \{#prepare-for-release\} Une fois votre intégration validée en sandbox, parcourez la checklist de mise en production pour vous assurer que tout est prêt. **Guide :** [Checklist de mise en production](release-checklist) À envoyer à votre LLM : ``` Read these Adapty docs before releasing: - https://adapty.io/docs/fr/release-checklist.md ``` :::tip[Checkpoint] - **Attendu :** Tous les éléments de la checklist sont confirmés : connexion au store, notifications serveur, flux d'achat, vérifications des niveaux d'accès et exigences de confidentialité. - **Piège :** App Store Server Notifications manquantes → configurez-les dans **App settings → iOS SDK** sinon les événements n'apparaîtront pas dans le tableau de bord. ::: ## Fichiers index de documentation en texte brut \{#plain-text-doc-index-files\} Si vous avez besoin de donner à votre LLM un contexte plus large au-delà des pages individuelles, nous hébergeons des fichiers index qui listent ou regroupent toute la documentation Adapty : - [`llms.txt`](https://adapty.io/docs/fr/llms.txt) : Liste toutes les pages avec des liens `.md`. Un [standard émergent](https://llmstxt.org/) pour rendre les sites web accessibles aux LLMs. Notez que pour certains agents IA (par ex. ChatGPT), vous devrez télécharger `llms.txt` et le joindre au chat en tant que fichier. - [`llms-full.txt`](https://adapty.io/docs/fr/llms-full.txt) : L'intégralité de la documentation Adapty regroupée en un seul fichier. Très volumineux — à utiliser uniquement quand vous avez besoin de la vue d'ensemble complète. - Sous-ensembles spécifiques iOS [`ios-llms.txt`](https://adapty.io/docs/fr/ios-llms.txt) et [`ios-llms-full.txt`](https://adapty.io/docs/fr/ios-llms-full.txt) : Des sous-ensembles par plateforme qui économisent des tokens par rapport au site complet. --- # File: ios-paywalls --- --- title: "Flows et paywalls - iOS" description: "Affichez et gérez les flows et paywalls créés avec l'Adapty Flow Builder ou le Paywall Builder dans votre app iOS." --- ## Afficher les paywalls \{#display-paywalls\} ### Adapty Flow Builder & Paywall Builder \{#adapty-flow-builder--paywall-builder\} :::tip Pour démarrer rapidement avec les paywalls Adapty Paywall Builder, consultez notre [guide de démarrage rapide](ios-quickstart-paywalls). ::: ### Implémenter les paywalls manuellement \{#implement-paywalls-manually\} Pour plus de guides sur l'implémentation des paywalls et la gestion des achats manuellement, consultez la [catégorie](ios-implement-paywalls-manually). ## Fonctionnalités utiles \{#useful-features\} --- # File: get-pb-paywalls --- --- title: "Récupérer les flows et paywalls - iOS" description: "Récupérez les flows et paywalls depuis Adapty dans votre app iOS." --- Après avoir [conçu votre flow ou votre paywall avec le Paywall Builder](adapty-paywall-builder), vous pouvez l'afficher dans votre app mobile. La première étape consiste à récupérer le flow ou le paywall associé au placement ainsi que sa configuration de vue, comme décrit ci-dessous. :::tip Vous voulez voir un exemple concret d'intégration du SDK Adapty dans une app mobile ? Consultez nos [apps d'exemple](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. :::
Avant de commencer 1. [Créez vos produits](create-product) dans l'Adapty Dashboard. 2. [Créez un flow/paywall et intégrez-y des produits](create-paywall) dans l'Adapty Dashboard. 3. [Créez des placements et intégrez-y votre flow/paywall](create-placement) dans l'Adapty Dashboard. 4. Installez le [SDK Adapty](sdk-installation-ios) dans votre app mobile.
## Récupérer un flow/paywall \{#fetch-flowpaywall\} Si vous avez conçu un flow ou un paywall avec le Flow Builder ou le Paywall Builder, vous n'avez pas à vous soucier de son rendu dans le code de votre app mobile. Un tel flow ou paywall contient à la fois ce qui doit être affiché et comment l'afficher. Vous devez néanmoins récupérer son ID via le placement, sa configuration de vue, puis le présenter dans votre app mobile. Récupérez le flow ou le paywall et sa [configuration de vue](get-pb-paywalls#fetch-the-view-configuration) le plus tôt possible — idéalement bien avant de l'afficher. Dès que vous récupérez la configuration de vue, le SDK commence à télécharger et mettre en cache ses images en arrière-plan. Plus vous la récupérez tôt, plus ces téléchargements ont le temps de se terminer. Au moment d'afficher le flow ou le paywall, sa configuration et ses images peuvent déjà être en cache et prêtes à l'emploi. Pour récupérer un flow ou un paywall, utilisez la méthode `getFlow` : ```swift showLineNumbers do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") // the requested flow/paywall } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): // the requested flow/paywall case let .failure(error): // handle the error } } ``` Paramètres : | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements) souhaité. C'est la valeur que vous avez indiquée lors de la création d'un placement dans l'Adapty Dashboard. | | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs ont une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs n'auront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé au redémarrage de l'app et n'est effacé que lors de la désinstallation ou d'un nettoyage manuel.

Le SDK Adapty stocke les paywalls localement en deux couches : le cache régulièrement mis à jour décrit ci-dessus et les [paywalls de secours](fallback-paywalls). Nous utilisons également un CDN pour récupérer les paywalls plus rapidement et un serveur de secours indépendant en cas d'inaccessibilité du CDN. Ce système est conçu pour vous garantir toujours la dernière version de vos paywalls tout en assurant la fiabilité même lorsque la connexion internet est limitée.

| | **loadTimeout** | par défaut : 5 s |

Cette valeur limite le délai d'attente de cette méthode. Si le délai est atteint, les données en cache ou le fallback local seront renvoyés.

Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut comprendre plusieurs requêtes en interne.

| Paramètres de réponse : | Paramètre | Description | | :-------- | :---------- | | Flow | Un objet `AdaptyFlow` contenant le placement, les identifiants (`id`, `variationId`), le nom, les Remote Configs et un indicateur `hasViewConfiguration` précisant si le flow inclut une configuration de vue. Pour récupérer les produits réels en vue d'un préchargement, d'une interface personnalisée ou de vérifications programmatiques, appelez `getPaywallProducts(flow:)`. | ## Récupérer la configuration de vue \{#fetch-the-view-configuration\} Après avoir récupéré le flow ou le paywall, vérifiez s'il inclut une configuration de vue via `flow.hasViewConfiguration`. Cet indicateur distingue la façon dont le placement a été conçu dans l'Adapty Dashboard : - **`true`** — le placement a été conçu dans le **Flow Builder** (un flow) ou le **Paywall Builder** (un paywall). Adapty génère l'interface pour vous. Continuez avec les étapes ci-dessous pour récupérer la configuration de vue et [présenter le flow ou le paywall](ios-present-paywalls). - **`false`** — le placement est un paywall personnalisé sans interface Builder. Utilisez la méthode `getFlowConfiguration` pour charger la configuration de vue. ```swift showLineNumbers guard flow.hasViewConfiguration else { // handle as remote config paywall return } let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow) ``` Paramètres : | Paramètre | Présence | Description | | :----------------------- | :------------- | :---------- | | **forFlow** | requis | Un objet `AdaptyFlow` obtenu via `Adapty.getFlow`. | | **locale** |

optionnel

par défaut : `nil`

| L'identifiant de la [localisation du paywall](add-paywall-locale-in-adapty-paywall-builder). Attendu sous la forme d'un code de langue avec un ou deux sous-tags séparés par `-` (ex. : `en`, `pt-br`). Voir [Localisations et codes de langue](localizations-and-locale-codes). | | **loadTimeout** | par défaut : 5 s | Cette valeur limite le délai d'attente de cette méthode. Si le délai est atteint, les données en cache ou le fallback local seront renvoyés. Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut comprendre plusieurs requêtes en interne. | | **products** | optionnel | Fournissez un tableau d'objets `AdaptyPaywallProduct` pour optimiser le moment d'affichage des produits à l'écran. Si `nil` est passé, AdaptyUI récupérera automatiquement les produits nécessaires. | | **systemRequestsHandler** | optionnel | Un objet conforme à `AdaptySystemRequestsHandler` qui gère les demandes d'autorisations système et d'évaluation déclenchées par les actions du flow. Requis uniquement si votre flow inclut de telles actions. | | **assetsResolver** | optionnel | Un dictionnaire `[String: AdaptyCustomAsset]` qui remplace les images et vidéos dans le flow/paywall. Voir [Personnaliser les assets](#customize-assets). | | **timerResolver** | optionnel | Un objet conforme à `AdaptyTimerResolver` qui fournit les dates de fin pour les timers définis par le développeur. Voir [Configurer les timers définis par le développeur](#set-up-developer-defined-timers). | Une fois chargé, [présentez le flow/paywall](ios-present-paywalls). ## Récupérer un flow ou un paywall pour l'audience par défaut afin d'accélérer la récupération \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} En général, les flows et paywalls sont récupérés presque instantanément, vous n'avez donc pas à vous soucier d'accélérer ce processus. Cependant, si vous avez de nombreuses audiences et placements et que vos utilisateurs ont une connexion lente, la récupération d'un flow ou d'un paywall peut prendre plus de temps que souhaité. Dans ce cas, vous pouvez afficher un flow ou un paywall par défaut pour garantir une expérience fluide plutôt que de ne rien afficher du tout. Pour cela, vous pouvez utiliser la méthode `getFlowForDefaultAudience`, qui récupère le flow ou le paywall du placement spécifié pour l'audience **All Users**. Il est toutefois essentiel de comprendre que l'approche recommandée reste de récupérer le flow ou le paywall avec la méthode `getFlow`, comme indiqué dans la section [Récupérer un flow/paywall](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) ci-dessus. :::warning Pourquoi nous recommandons d'utiliser `getFlow` La méthode `getFlowForDefaultAudience` présente quelques inconvénients importants : - **Problèmes potentiels de compatibilité ascendante** : Si vous devez afficher des paywalls différents selon les versions de l'app (actuelle et futures), vous pourrez rencontrer des difficultés. Vous devrez soit concevoir des paywalls compatibles avec la version actuelle (legacy), soit accepter que les utilisateurs de cette version puissent rencontrer des problèmes avec des paywalls non rendus. - **Perte de ciblage** : Tous les utilisateurs verront le même paywall conçu pour l'audience **All Users**, ce qui signifie que vous perdez le ciblage personnalisé (notamment basé sur les pays, l'attribution marketing ou vos propres attributs personnalisés). Si vous acceptez ces inconvénients pour bénéficier d'une récupération plus rapide du flow ou du paywall, utilisez la méthode `getFlowForDefaultAudience` comme suit. Sinon, utilisez `getFlow` décrit [ci-dessus](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder). ::: ```swift showLineNumbers Adapty.getFlowForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): // the requested flow case let .failure(error): // handle the error } } ``` | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez indiquée lors de la création d'un placement dans votre Adapty Dashboard. | | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs ont une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs n'auront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé au redémarrage de l'app et n'est effacé que lors de la désinstallation ou d'un nettoyage manuel.

| ## Personnaliser les assets \{#customize-assets\} Pour personnaliser les images et vidéos dans votre paywall/flow, implémentez les assets personnalisés. Les images et vidéos hero ont des IDs prédéfinis : `hero_image` et `hero_video`. Dans un bundle d'assets personnalisés, vous ciblez ces éléments par leurs IDs et personnalisez leur comportement. Pour les autres images et vidéos, vous devez [définir un ID personnalisé](custom-media) dans le tableau de bord Adapty. Par exemple, vous pouvez : - Afficher une image ou une vidéo différente à certains utilisateurs. - Afficher une image de prévisualisation locale pendant le chargement d'une image principale distante. - Afficher une image de prévisualisation avant de lancer une vidéo. - Fournir la résolution en pixels d'une vidéo afin que le lecteur réserve l'espace de mise en page (ratio = `width / height`) avant le chargement de la vidéo. Passez `nil` pour ignorer cela. Voici un exemple de fourniture d'assets personnalisés via un simple dictionnaire : ```swift showLineNumbers let customAssets: [String: AdaptyCustomAsset] = [ // Show a local image using a custom ID "custom_image": .image( .uiImage(value: UIImage(named: "image_name")!) ), // Show a local preview image while a remote main image is loading "hero_image": .image( .remote( url: URL(string: "https://example.com/image.jpg")!, preview: UIImage(named: "preview_image") ) ), // Show a local video with a preview image and a known resolution "hero_video": .video( .file( url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!, preview: .uiImage(value: UIImage(named: "video_preview")!), resolution: CGSize(width: 1080, height: 1920) ) ), ] let flowConfig = try await AdaptyUI.getFlowConfiguration( forFlow: flow, assetsResolver: customAssets ) ``` :::note Si un asset est introuvable, le paywall/flow utilisera son apparence par défaut. ::: ## Configurer les timers définis par le développeur \{#set-up-developer-defined-timers\} Pour utiliser des timers personnalisés dans votre app mobile, créez un objet conforme au protocole `AdaptyTimerResolver`. Cet objet définit comment chaque timer personnalisé doit être rendu. Si vous préférez, vous pouvez utiliser directement un dictionnaire `[String: Date]`, car il est déjà conforme à ce protocole. Voici un exemple : ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` Dans cet exemple, `CUSTOM_TIMER_NY` et `CUSTOM_TIMER_6H` sont les **Timer ID** des timers définis par le développeur que vous avez configurés dans l'Adapty Dashboard. Le `timerResolver` garantit que votre app met à jour dynamiquement chaque timer avec la valeur correcte. Par exemple : - `CUSTOM_TIMER_NY` : le temps restant jusqu'à la fin du timer, comme le Nouvel An. - `CUSTOM_TIMER_6H` : le temps restant dans une période de 6 heures qui a démarré lorsque l'utilisateur a ouvert le paywall.
Après avoir [conçu la partie visuelle de votre paywall](adapty-paywall-builder) avec le Paywall Builder dans l'Adapty Dashboard, vous pouvez l'afficher dans votre app mobile. La première étape consiste à récupérer le paywall associé au placement ainsi que sa configuration de vue, comme décrit ci-dessous. Notez que ce sujet concerne les paywalls personnalisés avec le Paywall Builder. Si vous implémentez vos paywalls manuellement, consultez [Récupérer les paywalls et produits pour les paywalls Remote Config](fetch-paywalls-and-products). :::tip Vous voulez voir un exemple concret d'intégration du SDK Adapty dans une app mobile ? Consultez nos [apps d'exemple](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. :::
Avant de commencer à afficher des paywalls dans votre app mobile 1. [Créez vos produits](create-product) dans l'Adapty Dashboard. 2. [Créez un paywall et intégrez-y les produits](create-paywall) dans l'Adapty Dashboard. 3. [Créez des placements et intégrez-y votre paywall](create-placement) dans l'Adapty Dashboard. 4. Installez le [SDK Adapty](sdk-installation-ios) dans votre app mobile.
## Récupérer un paywall conçu avec le Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\} Si vous avez [conçu un paywall avec le Paywall Builder](adapty-paywall-builder), vous n'avez pas à vous soucier de son rendu dans le code de votre app mobile. Un tel paywall contient à la fois ce qui doit être affiché et comment l'afficher. Vous devez néanmoins récupérer son ID via le placement, sa configuration de vue, puis le présenter dans votre app mobile. Pour des performances optimales, il est essentiel de récupérer le paywall et sa [configuration de vue](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) le plus tôt possible, afin de laisser suffisamment de temps aux images pour se télécharger avant de les présenter à l'utilisateur. Pour récupérer un paywall, utilisez la méthode `getPaywall` : ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` Paramètres : | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements) souhaité. C'est la valeur que vous avez indiquée lors de la création d'un placement dans l'Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la [localisation du paywall](add-paywall-locale-in-adapty-paywall-builder). Ce paramètre doit être un code de langue composé d'un ou deux sous-tags séparés par le caractère moins (**-**). Le premier sous-tag désigne la langue, le second la région.

Exemple : `en` signifie anglais, `pt-br` représente le portugais brésilien.

Voir [Localisations et codes de langue](localizations-and-locale-codes) pour plus d'informations sur les codes de langue et notre façon de les utiliser.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs ont une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs n'auront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé au redémarrage de l'app et n'est effacé que lors de la désinstallation ou d'un nettoyage manuel.

Le SDK Adapty stocke les paywalls localement en deux couches : le cache régulièrement mis à jour décrit ci-dessus et les [paywalls de secours](fallback-paywalls). Nous utilisons également un CDN pour récupérer les paywalls plus rapidement et un serveur de secours indépendant en cas d'inaccessibilité du CDN. Ce système est conçu pour vous garantir toujours la dernière version de vos paywalls tout en assurant la fiabilité même lorsque la connexion internet est limitée.

| | **loadTimeout** | par défaut : 5 s |

Cette valeur limite le délai d'attente de cette méthode. Si le délai est atteint, les données en cache ou le fallback local seront renvoyés.

Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut comprendre plusieurs requêtes en interne.

| Paramètres de réponse : | Paramètre | Description | | :-------- | :---------- | | Paywall | Un objet [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) avec une liste d'IDs de produits, l'identifiant du paywall, le Remote Config et plusieurs autres propriétés. | ## Récupérer la configuration de vue d'un paywall conçu avec le Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important Veillez à activer le bouton **Show on device** dans le Paywall Builder. Si cette option n'est pas activée, la configuration de vue ne sera pas disponible à la récupération. ::: Après avoir récupéré le paywall, vérifiez s'il inclut une configuration de vue, ce qui indique qu'il a été créé avec le Paywall Builder. Cela vous guidera sur la façon d'afficher le paywall. Si la configuration de vue est présente, traitez-le comme un paywall Paywall Builder ; sinon, [gérez-le comme un paywall Remote Config](present-remote-config-paywalls). Utilisez la méthode `getPaywallConfiguration` pour charger la configuration de vue. ```swift showLineNumbers guard paywall.hasViewConfiguration else { // use your custom logic return } do { let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, products: products ) // use loaded configuration } catch { // handle the error } ``` Paramètres : | Paramètre | Présence | Description | | :----------------------- | :------------- | :---------- | | **paywall** | requis | Un objet `AdaptyPaywall` pour obtenir un contrôleur pour le paywall souhaité. | | **loadTimeout** | par défaut : 5 s | Cette valeur limite le délai d'attente de cette méthode. Si le délai est atteint, les données en cache ou le fallback local seront renvoyés. Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut comprendre plusieurs requêtes en interne. | | **products** | optionnel | Fournissez un tableau d'objets `AdaptyPaywallProduct` pour optimiser le moment d'affichage des produits à l'écran. Si `nil` est passé, AdaptyUI récupérera automatiquement les produits nécessaires. | :::note Si vous utilisez plusieurs langues, découvrez comment ajouter une [localisation Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) et comment utiliser correctement les codes de langue [ici](localizations-and-locale-codes). ::: Une fois chargé, [présentez le paywall](ios-present-paywalls). ## Récupérer un paywall pour l'audience par défaut afin d'accélérer la récupération \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} En général, les paywalls sont récupérés presque instantanément, vous n'avez donc pas à vous soucier d'accélérer ce processus. Cependant, si vous avez de nombreuses audiences et paywalls et que vos utilisateurs ont une connexion lente, la récupération d'un paywall peut prendre plus de temps que souhaité. Dans ce cas, vous pouvez afficher un paywall par défaut pour garantir une expérience fluide plutôt que de ne rien afficher du tout. Pour cela, vous pouvez utiliser la méthode `getPaywallForDefaultAudience`, qui récupère le paywall du placement spécifié pour l'audience **All Users**. Il est toutefois essentiel de comprendre que l'approche recommandée reste de récupérer le paywall avec la méthode `getPaywall`, comme indiqué dans la section [Récupérer un paywall](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) ci-dessus. :::warning Pourquoi nous recommandons d'utiliser `getPaywall` La méthode `getPaywallForDefaultAudience` présente quelques inconvénients importants : - **Problèmes potentiels de compatibilité ascendante** : Si vous devez afficher des paywalls différents selon les versions de l'app (actuelle et futures), vous pourrez rencontrer des difficultés. Vous devrez soit concevoir des paywalls compatibles avec la version actuelle (legacy), soit accepter que les utilisateurs de cette version puissent rencontrer des problèmes avec des paywalls non rendus. - **Perte de ciblage** : Tous les utilisateurs verront le même paywall conçu pour l'audience **All Users**, ce qui signifie que vous perdez le ciblage personnalisé (notamment basé sur les pays, l'attribution marketing ou vos propres attributs personnalisés). Si vous acceptez ces inconvénients pour bénéficier d'une récupération plus rapide du paywall, utilisez la méthode `getPaywallForDefaultAudience` comme suit. Sinon, utilisez `getPaywall` décrit [ci-dessus](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder). ::: ```swift showLineNumbers Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` :::note La méthode `getPaywallForDefaultAudience` est disponible à partir de la version 2.11.2 du SDK iOS. ::: | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez indiquée lors de la création d'un placement dans votre Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la [localisation du paywall](add-remote-config-locale). Ce paramètre doit être un code de langue composé d'un ou plusieurs sous-tags séparés par le caractère moins (**-**). Le premier sous-tag désigne la langue, le second la région.

Exemple : `en` signifie anglais, `pt-br` représente le portugais brésilien.

Voir [Localisations et codes de langue](localizations-and-locale-codes) pour plus d'informations sur les codes de langue et notre façon de les utiliser.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs ont une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs n'auront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé au redémarrage de l'app et n'est effacé que lors de la désinstallation ou d'un nettoyage manuel.

| ## Personnaliser les assets \{#customize-assets\} Pour personnaliser les images et vidéos dans votre paywall, implémentez les assets personnalisés. Les images et vidéos hero ont des IDs prédéfinis : `hero_image` et `hero_video`. Dans un bundle d'assets personnalisés, vous ciblez ces éléments par leurs IDs et personnalisez leur comportement. Pour les autres images et vidéos, vous devez [définir un ID personnalisé](custom-media) dans le tableau de bord Adapty. Par exemple, vous pouvez : - Afficher une image ou une vidéo différente à certains utilisateurs. - Afficher une image de prévisualisation locale pendant le chargement d'une image principale distante. - Afficher une image de prévisualisation avant de lancer une vidéo. :::important Pour utiliser cette fonctionnalité, mettez à jour le SDK iOS Adapty vers la version 3.7.0 ou supérieure. ::: Voici un exemple de fourniture d'assets personnalisés via un simple dictionnaire : ```swift showLineNumbers let customAssets: [String: AdaptyCustomAsset] = [ // Show a local image using a custom ID "custom_image": .image( .uiImage(value: UIImage(named: "image_name")!) ), // Show a local preview image while a remote main image is loading "hero_image": .image( .remote( url: URL(string: "https://example.com/image.jpg")!, preview: UIImage(named: "preview_image") ) ), // Show a local video with a preview image "hero_video": .video( .file( url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!, preview: .uiImage(value: UIImage(named: "video_preview")!) ) ), ] let paywallConfig = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, assetsResolver: customAssets ) ``` :::note Si un asset est introuvable, le paywall utilisera son apparence par défaut. ::: ## Configurer les timers définis par le développeur \{#set-up-developer-defined-timers\} Pour utiliser des timers personnalisés dans votre app mobile, créez un objet conforme au protocole `AdaptyTimerResolver`. Cet objet définit comment chaque timer personnalisé doit être rendu. Si vous préférez, vous pouvez utiliser directement un dictionnaire `[String: Date]`, car il est déjà conforme à ce protocole. Voici un exemple : ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` Dans cet exemple, `CUSTOM_TIMER_NY` et `CUSTOM_TIMER_6H` sont les **Timer ID** des timers définis par le développeur que vous avez configurés dans l'Adapty Dashboard. Le `timerResolver` garantit que votre app met à jour dynamiquement chaque timer avec la valeur correcte. Par exemple : - `CUSTOM_TIMER_NY` : le temps restant jusqu'à la fin du timer, comme le Nouvel An. - `CUSTOM_TIMER_6H` : le temps restant dans une période de 6 heures qui a démarré lorsque l'utilisateur a ouvert le paywall.
--- # File: ios-present-paywalls --- --- title: "Afficher les flows & paywalls - iOS" description: "Présentez les flows et paywalls aux utilisateurs dans votre application iOS." --- Si vous avez créé un flow ou un paywall, vous n'avez pas à vous soucier de son rendu dans le code de votre application mobile pour l'afficher à l'utilisateur. Un tel flow ou paywall contient à la fois ce qui doit être affiché et comment cela doit l'être. Pour obtenir l'objet `AdaptyUI.FlowConfiguration` utilisé ci-dessous, consultez [Récupérer les flows et paywalls](get-pb-paywalls). ## Présenter les flows et paywalls avec SwiftUI \{#present-flows-and-paywalls-in-swiftui\} ### Présenter comme une vue modale \{#present-as-a-modal-view\} Pour afficher un flow ou un paywall sur l'écran de l'appareil comme une vue modale, utilisez le modificateur `.flow` dans SwiftUI. L'appel minimal requiert `isPresented`, `flowConfiguration` et les cinq callbacks obligatoires : ```swift showLineNumbers title="SwiftUI" .flow( isPresented: $flowPresented, flowConfiguration: , didFinishPurchase: { _, _ in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { _, _ in /* handle the error */ }, didFinishRestore: { _ in /* check access level and dismiss */ }, didFailRestore: { _ in /* handle the error */ }, didReceiveError: { _ in flowPresented = false } ) ``` Pour plus de contrôle, ajoutez des callbacks optionnels comme `didPerformAction` pour gérer les appuis sur les boutons : ```swift showLineNumbers title="SwiftUI" @State var flowPresented = false // ensure that you manage this variable state and set it to `true` at the moment you want to show the flow or paywall var body: some View { Text("Hello, AdaptyUI!") .flow( isPresented: $flowPresented, flowConfiguration: , didPerformAction: { action in switch action { case .close: flowPresented = false default: // Handle other actions break } }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) } ``` Paramètres : | Paramètre | Requis | Description | |:-----------------------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requis | Un binding qui détermine si l'écran du flow ou du paywall est affiché. | | **flowConfiguration** | requis | Un objet `AdaptyUI.FlowConfiguration` contenant les détails visuels du flow ou du paywall. Utilisez la méthode `AdaptyUI.getFlowConfiguration(forFlow:)`. Consultez [Récupérer les flows et paywalls](get-pb-paywalls) pour plus de détails. | | **didFinishPurchase** | requis | Invoqué lorsque `Adapty.makePurchase()` se termine avec succès. Le flow ne se ferme pas automatiquement — mettez votre binding de présentation à `false` ici, ou ne faites rien pour laisser le flow continuer après l'achat. | | **didFailPurchase** | requis | Invoqué lorsque `Adapty.makePurchase()` échoue. | | **didFinishRestore** | requis | Invoqué lorsque `Adapty.restorePurchases()` se termine avec succès. | | **didFailRestore** | requis | Invoqué lorsque `Adapty.restorePurchases()` échoue. | | **didReceiveError** | requis | Invoqué en cas d'erreur de rendu ou d'erreur d'exécution provenant du script du flow (par exemple, une exception JavaScript, code `AdaptyUIError` `4105`). Pour les erreurs de rendu, [contactez le support Adapty](mailto:support@adapty.io). | | **fullScreen** | optionnel | Détermine si le flow ou le paywall s'affiche en plein écran ou sous forme de feuille. Par défaut : `true`. | | **didAppear** | optionnel | Invoqué lorsque la vue du flow ou du paywall a été présentée. | | **didDisappear** | optionnel | Invoqué lorsque la vue du flow ou du paywall a été fermée. | | **didPerformAction** | optionnel | Invoqué lorsqu'un utilisateur clique sur un bouton. Deux identifiants d'action sont prédéfinis : `close` et `openURL` ; les autres sont personnalisés et peuvent être définis dans le builder. | | **didSelectProduct** | optionnel | Invoqué lorsqu'un produit est sélectionné pour l'achat par l'utilisateur ou par le système. | | **didStartPurchase** | optionnel | Invoqué lorsque l'utilisateur commence le processus d'achat. | | **didFinishWebPaymentNavigation** | optionnel | Invoqué lorsque la navigation de paiement web se termine. | | **didStartRestore** | optionnel | Invoqué lorsque l'utilisateur démarre le processus de restauration. | | **didFailLoadingProducts** | optionnel | Invoqué lorsque des erreurs surviennent lors du chargement des produits. Retournez `true` pour relancer le chargement. | | **didPartiallyLoadProducts** | optionnel | Invoqué lorsque les produits sont partiellement chargés. | | **showAlertItem** | optionnel | Un binding qui gère l'affichage des éléments d'alerte au-dessus du flow ou du paywall. | | **showAlertBuilder** | optionnel | Une fonction pour afficher la vue d'alerte. | | **placeholderBuilder** | optionnel | Une fonction pour afficher la vue de remplacement pendant le chargement du flow ou du paywall. Par défaut : une `ProgressView`. | Consultez la rubrique [iOS - Gestion des événements](ios-handling-events) pour plus de détails sur les paramètres. ### Présenter comme une vue non modale \{#present-as-a-non-modal-view\} Vous pouvez également présenter les flows et paywalls comme destinations de navigation ou vues intégrées dans le flux de navigation de votre application. Utilisez `AdaptyFlowView` directement dans vos vues SwiftUI : ```swift showLineNumbers title="SwiftUI" AdaptyFlowView( flowConfiguration: , didFinishPurchase: { product, purchaseResult in // Dismiss the view, or do nothing to let the flow continue }, didFailPurchase: { product, error in // Handle purchase failure }, didFinishRestore: { profile in // Handle successful restore }, didFailRestore: { error in // Handle restore failure }, didReceiveError: { error in // Handle the error (rendering or JS exception from the flow script). } ) ``` ## Présenter les flows et paywalls avec UIKit \{#present-flows-and-paywalls-in-uikit\} Pour afficher le flow ou le paywall sur l'écran de l'appareil, procédez comme suit : 1. Initialisez le flow visuel que vous souhaitez afficher en utilisant la méthode `AdaptyUI.flowController(with:delegate:)` : ```swift showLineNumbers title="Swift" import AdaptyUI let visualFlow = try AdaptyUI.flowController( with: , delegate: ) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :----------------------- | :------- | :---------- | | **flowConfiguration** | requis | Un objet `AdaptyUI.FlowConfiguration` contenant les détails visuels du flow ou du paywall. Utilisez la méthode `AdaptyUI.getFlowConfiguration(forFlow:)`. Consultez la rubrique [Récupérer les flows et paywalls](get-pb-paywalls) pour plus de détails. | | **delegate** | requis | Un `AdaptyFlowControllerDelegate` pour écouter les événements du flow et du paywall. Consultez la rubrique [Gérer les événements de flow & paywall](ios-handling-events) pour plus de détails. | Valeur retournée : | Objet | Description | | :---------------------- | :----------------------------------------------------------------- | | **AdaptyFlowController** | Un objet représentant l'écran du flow ou du paywall demandé. | 2. Une fois l'objet créé avec succès, vous pouvez l'afficher sur l'écran de l'appareil : ```swift showLineNumbers title="Swift" present(visualFlow, animated: true) ``` :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: Si vous avez personnalisé un paywall avec le Paywall Builder, vous n'avez pas à vous soucier de son rendu dans le code de votre application mobile pour l'afficher à l'utilisateur. Un tel paywall contient à la fois ce qui doit être affiché et comment cela doit l'être. Pour obtenir l'objet `AdaptyUI.PaywallConfiguration` utilisé ci-dessous, consultez [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls). ## Présenter les paywalls avec SwiftUI \{#present-paywalls-in-swiftui\} ### Présenter comme une vue modale \{#present-as-a-modal-view\} Pour afficher le paywall visuel sur l'écran de l'appareil comme une vue modale, utilisez le modificateur `.paywall` dans SwiftUI : ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false // ensure that you manage this variable state and set it to `true` at the moment you want to show the paywall var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywallConfiguration: , didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishPurchase: { product, profile in paywallPresented = false }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } ) } ``` Paramètres : | Paramètre | Requis | Description | |:----------------------------------|:-------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requis | Un binding qui détermine si l'écran du paywall est affiché. | | **paywallConfiguration** | requis | Un objet `AdaptyUI.PaywallConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **didFailPurchase** | requis | Invoqué lorsque `Adapty.makePurchase()` échoue. | | **didFinishRestore** | requis | Invoqué lorsque `Adapty.restorePurchases()` se termine avec succès. | | **didFailRestore** | requis | Invoqué lorsque `Adapty.restorePurchases()` échoue. | | **didFailRendering** | requis | Invoqué si une erreur survient lors du rendu de l'interface. Dans ce cas, [contactez le support Adapty](mailto:support@adapty.io). | | **fullScreen** | optionnel | Détermine si le paywall s'affiche en plein écran ou sous forme de modal. Par défaut : `true`. | | **didAppear** | optionnel | Invoqué lorsque la vue du paywall a été présentée. | | **didDisappear** | optionnel | Invoqué lorsque la vue du paywall a été fermée. | | **didPerformAction** | optionnel | Invoqué lorsqu'un utilisateur clique sur un bouton. Différents boutons ont différents identifiants d'action. Deux identifiants d'action sont prédéfinis : `close` et `openURL`, les autres sont personnalisés et peuvent être définis dans le builder. | | **didSelectProduct** | optionnel | Invoqué lorsqu'un produit est sélectionné pour l'achat par l'utilisateur ou par le système. | | **didStartPurchase** | optionnel | Invoqué lorsque l'utilisateur commence le processus d'achat. | | **didFinishPurchase** | optionnel | Invoqué lorsque `Adapty.makePurchase()` se termine avec succès. | | **didFinishWebPaymentNavigation** | optionnel | Invoqué lorsque la navigation de paiement web se termine. | | **didStartRestore** | optionnel | Invoqué lorsque l'utilisateur démarre le processus de restauration. | | **didFailLoadingProducts** | optionnel | Invoqué lorsque des erreurs surviennent lors du chargement des produits. Retournez `true` pour relancer le chargement. | | **didPartiallyLoadProducts** | optionnel | Invoqué lorsque les produits sont partiellement chargés. | | **showAlertItem** | optionnel | Un binding qui gère l'affichage des éléments d'alerte au-dessus du paywall. | | **showAlertBuilder** | optionnel | Une fonction pour afficher la vue d'alerte. | | **placeholderBuilder** | optionnel | Une fonction pour afficher la vue de remplacement pendant le chargement du paywall. | Consultez la rubrique [iOS - Gestion des événements](ios-handling-events) pour plus de détails sur les paramètres. ### Présenter comme une vue non modale \{#present-as-a-non-modal-view\} Vous pouvez également présenter les paywalls comme destinations de navigation ou vues intégrées dans le flux de navigation de votre application. Utilisez `AdaptyPaywallView` directement dans vos vues SwiftUI : ```swift showLineNumbers title="SwiftUI" AdaptyPaywallView( paywallConfiguration: , didFailPurchase: { product, error in // Handle purchase failure }, didFinishRestore: { profile in // Handle successful restore }, didFailRestore: { error in // Handle restore failure }, didFailRendering: { error in // Handle rendering error } ) ``` ## Présenter les paywalls avec UIKit \{#present-paywalls-in-uikit\} Pour afficher le paywall visuel sur l'écran de l'appareil, procédez comme suit : 1. Initialisez le paywall visuel que vous souhaitez afficher en utilisant la méthode `.paywallController(for:products:viewConfiguration:delegate:)` : ```swift showLineNumbers title="Swift" import AdaptyUI let visualPaywall = AdaptyUI.paywallController( with: , delegate: ) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :----------------------- | :------- | :---------- | | **paywall configuration** | requis | Un objet `AdaptyUI.PaywallConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **delegate** | requis | Un `AdaptyPaywallControllerDelegate` pour écouter les événements du paywall. Consultez la rubrique [Gestion des événements de paywall](ios-handling-events) pour plus de détails. Valeur retournée : | Objet | Description | | :---------------------- | :------------------------------------------------------- | | **AdaptyPaywallController** | Un objet représentant l'écran du paywall demandé. | 2. Une fois l'objet créé avec succès, vous pouvez l'afficher sur l'écran de l'appareil : ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: --- # File: handle-paywall-actions --- --- title: "Répondre aux actions des flows - iOS" description: "Gérez les actions des boutons et traitez les entrées utilisateur dans les flows de paywall et d'onboarding de votre app iOS." --- Si vous créez des flows ou des paywalls avec l'Adapty Flow Builder ou le Paywall Builder, il est essentiel de configurer correctement les boutons : 1. Ajoutez un [bouton dans le Paywall Builder](paywall-buttons) et assignez-lui une action existante ou créez un ID d'action personnalisé. 2. Écrivez le code dans votre app pour gérer chaque action assignée. Ce guide montre comment gérer les actions personnalisées et les actions existantes dans votre code. :::warning **Seules la fermeture des flows/paywalls et l'ouverture des URL sont gérées automatiquement.** Toutes les autres actions de bouton nécessitent une implémentation appropriée dans le code de l'app. ::: :::note Le SDK iOS peut répondre aux demandes de permissions système, comme les notifications push ou l'accès à la caméra, via un `AdaptySystemRequestsHandler`. Les flows ne déclenchent pas encore ces demandes, vous n'avez donc pas besoin de les gérer pour l'instant. ::: ## Fermer les flows et les paywalls \{#close-flows-and-paywalls\} Pour ajouter un bouton qui ferme votre flow ou paywall : 1. Dans le builder, ajoutez un bouton et assignez-lui l'action **Close**. 2. Dans le code de votre app, implémentez un gestionnaire pour l'action `close`. :::info Dans le SDK iOS, l'action `close` déclenche par défaut la fermeture du flow ou du paywall. Vous pouvez toutefois remplacer ce comportement dans votre code si nécessaire. Par exemple, fermer un flow peut déclencher l'ouverture d'un autre. ::: ```swift .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false // dismiss the flow or paywall default: break } }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) ``` ## Ouvrir des URL depuis les flows et les paywalls \{#open-urls-from-flows-and-paywalls\} :::tip Si vous souhaitez ajouter un groupe de liens (par ex. conditions d'utilisation et restauration des achats), ajoutez un élément **Link** dans le builder et gérez-le de la même façon que les boutons avec l'action **Open URL**. ::: Pour ajouter un bouton qui ouvre un lien depuis votre flow ou paywall (par ex. **Terms of use** ou **Privacy policy**) : 1. Dans le builder, ajoutez un bouton, assignez-lui l'action **Open URL** et saisissez l'URL à ouvrir. 2. Dans le code de votre app, implémentez un gestionnaire pour l'action `openURL` qui ouvre l'URL reçue dans un navigateur. :::info Dans le SDK iOS, l'action `openURL` déclenche par défaut l'ouverture de l'URL. Vous pouvez toutefois remplacer ce comportement dans votre code si nécessaire. ::: ```swift .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case let .openURL(url): UIApplication.shared.open(url, options: [:]) // default behavior default: break } }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) ``` ## Gérer les actions personnalisées \{#handle-custom-actions\} Pour ajouter un bouton qui gère d'autres actions : 1. Dans le builder, ajoutez un bouton, assignez-lui l'action **Custom** et donnez-lui un ID. 2. Dans le code de votre app, implémentez un gestionnaire pour l'ID d'action que vous avez créé. Par exemple, si vous avez un autre ensemble d'offres d'abonnement ou d'achats uniques, vous pouvez ajouter un bouton qui affiche un autre flow ou paywall : ```swift .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case let .custom(id): if id == "openNewPaywall" { // Display another flow or paywall } default: break } }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) ``` Si vous créez des paywalls avec le Paywall Builder Adapty, il est essentiel de configurer correctement les boutons : 1. Ajoutez un [bouton dans le Paywall Builder](paywall-buttons) et assignez-lui une action existante ou créez un ID d'action personnalisé. 2. Écrivez le code dans votre app pour gérer chaque action assignée. Ce guide montre comment gérer les actions personnalisées et les actions existantes dans votre code. :::warning **Seules les achats, les restaurations, la fermeture des paywalls et l'ouverture des URL sont gérés automatiquement.** Toutes les autres actions de bouton nécessitent une implémentation appropriée dans le code de l'app. ::: ## Fermer les paywalls \{#close-paywalls\} Pour ajouter un bouton qui ferme votre paywall : 1. Dans le Paywall Builder, ajoutez un bouton et assignez-lui l'action **Close**. 2. Dans le code de votre app, implémentez un gestionnaire pour l'action `close` qui ferme le paywall. :::info Dans le SDK iOS, l'action `close` déclenche par défaut la fermeture du paywall. Vous pouvez toutefois remplacer ce comportement dans votre code si nécessaire. Par exemple, fermer un paywall peut déclencher l'ouverture d'un autre. ::: ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .close: controller.dismiss(animated: true) // default behavior break } } ``` ## Ouvrir des URL depuis les paywalls \{#open-urls-from-paywalls\} :::tip Si vous souhaitez ajouter un groupe de liens (par ex. conditions d'utilisation et restauration des achats), ajoutez un élément **Link** dans le Paywall Builder et gérez-le de la même façon que les boutons avec l'action **Open URL**. ::: Pour ajouter un bouton qui ouvre un lien depuis votre paywall (par ex. **Terms of use** ou **Privacy policy**) : 1. Dans le Paywall Builder, ajoutez un bouton, assignez-lui l'action **Open URL** et saisissez l'URL à ouvrir. 2. Dans le code de votre app, implémentez un gestionnaire pour l'action `openUrl` qui ouvre l'URL reçue dans un navigateur. :::info Dans le SDK iOS, l'action `openUrl` déclenche par défaut l'ouverture de l'URL. Vous pouvez toutefois remplacer ce comportement dans votre code si nécessaire. ::: ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .openURL(url): UIApplication.shared.open(url, options: [:]) // default behavior break } } ``` ## Se connecter à l'app \{#log-into-the-app\} Pour ajouter un bouton qui connecte les utilisateurs à votre app : 1. Dans le Paywall Builder, ajoutez un bouton et assignez-lui l'action **Login**. 2. Dans le code de votre app, implémentez un gestionnaire pour l'action `login` qui identifie votre utilisateur. ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .login: // Show a login screen let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController") controller.present(loginVC, animated: true) } } ``` ## Gérer les actions personnalisées \{#handle-custom-actions\} Pour ajouter un bouton qui gère d'autres actions : 1. Dans le Paywall Builder, ajoutez un bouton, assignez-lui l'action **Custom** et donnez-lui un ID. 2. Dans le code de votre app, implémentez un gestionnaire pour l'ID d'action que vous avez créé. Par exemple, si vous avez un autre ensemble d'offres d'abonnement ou d'achats uniques, vous pouvez ajouter un bouton qui affiche un autre paywall : ```swift func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case let .custom(id): if id == "openNewPaywall" { // Display another paywall } } break } } ``` --- # File: ios-handling-events --- --- title: "Gérer les événements de flow et de paywall - iOS" description: "Gérez les événements de flow et de paywall dans votre application iOS." --- :::important Ce guide couvre la gestion des événements pour les achats, les restaurations, la sélection de produits et l'affichage des paywalls. Vous devez également implémenter la gestion des boutons (fermeture du paywall, ouverture de liens, etc.). Consultez notre [guide sur la gestion des actions de boutons](handle-paywall-actions) pour en savoir plus. ::: Les flows et les paywalls n'ont pas besoin de code supplémentaire pour effectuer et restaurer des achats. Cependant, ils génèrent certains événements auxquels votre application peut réagir. Ces événements incluent les pressions sur des boutons (boutons de fermeture, URLs, sélections de produits, etc.) ainsi que des notifications sur les actions liées aux achats. Découvrez ci-dessous comment répondre à ces événements. :::tip Vous voulez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [applications exemples](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et les autres fonctionnalités de base. ::: ## Gestion des événements dans SwiftUI \{#handling-events-in-swiftui\} Pour contrôler ou surveiller les processus qui se déroulent sur l'écran de flow ou de paywall dans votre application mobile, utilisez le modificateur `.flow` dans SwiftUI : ```swift showLineNumbers title="Swift" @State var flowPresented = false var body: some View { Text("Hello, AdaptyUI!") .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false case let .openURL(url): // handle opening the URL (incl. for terms and privacy) default: // handle other actions } }, didSelectProduct: { product in /* Handle the event */ }, didStartPurchase: { product in /* Handle the event */ }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didStartRestore: { /* Handle the event */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false }, didFailLoadingProducts: { error in // Return `true` to retry loading return false } ) } ``` Vous pouvez n'enregistrer que les paramètres de fermeture dont vous avez besoin et omettre ceux dont vous n'avez pas besoin. | Paramètre | Requis | Description | |:-----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requis | Un binding qui détermine si l'écran du flow ou du paywall est affiché. | | **flowConfiguration** | requis | Un objet `AdaptyUI.FlowConfiguration` contenant les détails visuels du flow ou du paywall. Consultez [Obtenir des flows et des paywalls](get-pb-paywalls) pour plus de détails. | | **didFinishPurchase** | requis | Appelé lorsque `Adapty.makePurchase()` se termine avec succès. Le flow ne se ferme pas automatiquement — définissez votre binding de présentation sur `false` ici, ou ne faites rien pour laisser le flow continuer après l'achat. | | **didFailPurchase** | requis | Appelé lorsque `Adapty.makePurchase()` échoue. | | **didFinishRestore** | requis | Appelé lorsque `Adapty.restorePurchases()` se termine avec succès. | | **didFailRestore** | requis | Appelé lorsque `Adapty.restorePurchases()` échoue. | | **didReceiveError** | requis | Appelé lorsque le flow rencontre une erreur de rendu ou une erreur d'exécution provenant du script du flow (par exemple, une exception JavaScript, code `AdaptyUIError` `4105`). En cas d'erreur de rendu, [contactez le support Adapty](mailto:support@adapty.io). | | **placeholderBuilder** | optionnel | Une fonction pour afficher la vue de remplacement pendant le chargement du flow ou du paywall. Par défaut, une `ProgressView`. | | **fullScreen** | optionnel | Détermine si le flow ou le paywall s'affiche en plein écran ou sous forme de feuille. Par défaut `true`. | | **didAppear** | optionnel | Appelé lorsque la vue du flow ou du paywall apparaît à l'écran. | | **didDisappear** | optionnel | Appelé lorsque la vue du flow ou du paywall a été fermée. | | **didPerformAction** | optionnel | Appelé lorsqu'un utilisateur clique sur un bouton. Deux identifiants d'action sont prédéfinis : `close` et `openURL` ; les autres sont personnalisés et peuvent être définis dans le builder. | | **didSelectProduct** | optionnel | Appelé lorsqu'un produit est sélectionné pour l'achat par l'utilisateur ou par le système. | | **didStartPurchase** | optionnel | Appelé lorsque l'utilisateur commence le processus d'achat. | | **didFinishWebPaymentNavigation** | optionnel | Appelé lorsque la navigation de paiement web se termine. | | **didStartRestore** | optionnel | Appelé lorsque l'utilisateur démarre le processus de restauration. | | **didFailLoadingProducts** | optionnel | Appelé lorsque des erreurs surviennent pendant le chargement des produits. Retournez `true` pour relancer le chargement. | | **didPartiallyLoadProducts** | optionnel | Appelé lorsque les produits sont partiellement chargés. | | **showAlertItem** | optionnel | Un binding qui gère l'affichage des éléments d'alerte au-dessus du flow ou du paywall. | | **showAlertBuilder** | optionnel | Une fonction pour afficher la vue d'alerte. | ## Gestion des événements dans UIKit \{#handling-events-in-uikit\} Pour les applications UIKit, les événements sont gérés via le protocole `AdaptyFlowControllerDelegate`. Consultez [Afficher les flows & paywalls - iOS](ios-present-paywalls) pour savoir comment configurer `AdaptyFlowController` avec `AdaptyFlowControllerDelegate`. Le protocole déclare 13 méthodes. Quatre d'entre elles n'ont pas d'implémentation par défaut et doivent être implémentées lors de la conformité : `didFinishPurchase`, `didFailPurchase`, `didFinishRestoreWith` et `didFailRestoreWith`. Les autres fournissent des implémentations no-op par défaut et peuvent être remplacées si vous souhaitez un comportement personnalisé. Les méthodes sont regroupées ci-dessous par objectif. ### Cycle de vie \{#lifecycle\} ```swift showLineNumbers title="Swift" func flowControllerDidAppear(_ controller: AdaptyFlowController) { } func flowControllerDidDisappear(_ controller: AdaptyFlowController) { } ``` Ces méthodes se déclenchent lorsque la vue du flow ou du paywall est affichée ou masquée. ### Actions utilisateur \{#user-actions\} ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didPerform action: AdaptyUI.Action ) { } ``` Cas de `AdaptyUI.Action` : - `.close` — par défaut, ferme le contrôleur. Surchargez cette action pour maintenir le contrôleur à l'écran ou effectuer un nettoyage supplémentaire. - `.openURL(url:)` — par défaut, ouvre l'URL avec `UIApplication.shared.open(...)`. - `.custom(id:)` — déclenché pour les boutons avec un identifiant d'action personnalisé défini dans le builder. ### Sélection du produit \{#product-selection\} ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didSelectProduct product: AdaptyPaywallProduct ) { } ``` Invoqué lorsqu'un produit est sélectionné pour achat par l'utilisateur ou par le système. Le produit contient toutes les informations sur l'offre (l'éligibilité est déterminée automatiquement en v4 — il n'existe pas de type `AdaptyPaywallProductWithoutDeterminingOffer` distinct). ### Événements d'achat \{#purchase-events\} ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didStartPurchase product: AdaptyPaywallProduct ) { } func flowController( _ controller: AdaptyFlowController, didFinishPurchase product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult ) { if !purchaseResult.isPurchaseCancelled { controller.dismiss(animated: true) // or do nothing to let the flow continue } } func flowController( _ controller: AdaptyFlowController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError ) { } ``` `didFinishPurchase` et `didFailPurchase` n'ont pas d'implémentation par défaut et doivent être implémentés. Le contrôleur ne se ferme pas automatiquement après un achat réussi — appelez `controller.dismiss(animated:)` le moment venu, ou ne faites rien pour laisser un flow multi-écrans continuer après l'achat. ### Événements de restauration \{#restore-events\} ```swift showLineNumbers title="Swift" func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { } func flowController( _ controller: AdaptyFlowController, didFinishRestoreWith profile: AdaptyProfile ) { } func flowController( _ controller: AdaptyFlowController, didFailRestoreWith error: AdaptyError ) { } ``` `didFinishRestoreWith` et `didFailRestoreWith` n'ont pas d'implémentation par défaut. Vérifiez que le `AdaptyProfile` retourné contient le niveau d'accès souhaité avant de fermer le contrôleur. ### Erreurs de flow et erreurs de chargement des produits \{#flow-errors-and-product-loading-errors\} ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didReceiveError error: AdaptyUIError ) { } func flowController( _ controller: AdaptyFlowController, didFailLoadingProductsWith error: AdaptyError ) -> Bool { // Return `true` to retry product loading; default returns `false`. return false } func flowController( _ controller: AdaptyFlowController, didPartiallyLoadProducts failedIds: [String] ) { } ``` `didReceiveError` se déclenche pour les erreurs de rendu et les erreurs d'exécution provenant du script du flow (exceptions JavaScript, code `AdaptyUIError` `4105`). Pour les erreurs de rendu, [contactez le support Adapty](mailto:support@adapty.io). Pour les erreurs de chargement, retournez `true` depuis `didFailLoadingProductsWith` pour réessayer — utile en cas de problèmes réseau passagers. ### Navigation de paiement web \{#web-payment-navigation\} ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didFinishWebPaymentNavigation product: AdaptyPaywallProduct?, error: AdaptyError? ) { } ``` Invoqué après la fin d'une navigation de paiement web, qu'elle ait réussi ou échoué. :::important Ce guide couvre la gestion des événements pour les achats, les restaurations, la sélection de produits et le rendu des paywalls. Vous devez également implémenter la gestion des boutons (fermeture du paywall, ouverture de liens, etc.). Consultez notre [guide sur la gestion des actions des boutons](handle-paywall-actions) pour plus de détails. ::: Les paywalls configurés avec le [Paywall Builder](adapty-paywall-builder) n'ont pas besoin de code supplémentaire pour effectuer et restaurer des achats. Cependant, ils génèrent certains événements auxquels votre application peut réagir. Ces événements incluent les pressions sur des boutons (boutons de fermeture, URL, sélections de produits, etc.) ainsi que des notifications sur les actions liées aux achats effectuées sur le paywall. Découvrez ci-dessous comment réagir à ces événements. Ce guide concerne uniquement les **paywalls du nouveau Paywall Builder**, qui nécessitent le SDK Adapty v3.0 ou une version ultérieure. :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: ## Gestion des événements dans SwiftUI \{#handling-events-in-swiftui\} Pour contrôler ou surveiller les processus se déroulant sur l'écran du paywall dans votre application mobile, utilisez le modificateur `.paywall` dans SwiftUI : ```swift showLineNumbers title="Swift" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywall: paywall, viewConfiguration: viewConfig, didPerformAction: { action in switch action { case .close: paywallPresented = false case let .openURL(url): // handle opening the URL (incl. for terms and privacy) default: // handle other actions } }, didSelectProduct: { /* Handle the event */ }, didStartPurchase: { /* Handle the event */ }, didFinishPurchase: { product, info in /* Handle the event */ }, didFailPurchase: { product, error in /* Handle the event */ }, didStartRestore: { /* Handle the event */ }, didFinishRestore: { /* Handle the event */ }, didFailRestore: { /* Handle the event */ }, didFailRendering: { error in paywallPresented = false }, didFailLoadingProducts: { error in return false } ) } ``` Vous pouvez n'enregistrer que les paramètres de closure dont vous avez besoin, et omettre ceux dont vous n'avez pas besoin. Dans ce cas, les paramètres de closure inutilisés ne seront pas créés. | Paramètre | Requis | Description | |:----------------------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **isPresented** | requis | Un binding qui gère l'affichage ou non de l'écran du paywall. | | **paywallConfiguration** | requis | Un objet `AdaptyUI.PaywallConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Consultez la rubrique [Récupérer les paywalls du Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **didFailPurchase** | requis | Déclenché lorsqu'un achat échoue en raison d'erreurs (ex. : paiement non autorisé, problème réseau, produit invalide). Non déclenché en cas d'annulation par l'utilisateur ou de paiement en attente. | | **didFinishRestore** | requis | Déclenché lorsqu'un achat se termine avec succès. | | **didFailRestore** | requis | Déclenché lorsque la restauration d'un achat échoue. | | **didFailRendering** | requis | Déclenché si une erreur survient lors du rendu de l'interface. Dans ce cas, [contactez le support Adapty](mailto:support@adapty.io). | | **fullScreen** | optionnel | Détermine si le paywall s'affiche en plein écran ou en modal. Par défaut à `true`. | | **didAppear** | optionnel | Déclenché lorsque la vue du paywall apparaît à l'écran. Également déclenché lorsqu'un utilisateur appuie sur le [bouton de paywall web](web-paywall#step-2a-add-a-web-purchase-button) dans un paywall et qu'un paywall web s'ouvre dans un navigateur intégré. | | **didDisappear** | optionnel | Déclenché lorsque la vue du paywall est fermée. Également déclenché lorsqu'un [paywall web](web-paywall#step-2a-add-a-web-purchase-button) ouvert depuis un paywall dans un navigateur intégré disparaît de l'écran. | | **didPerformAction** | optionnel | Déclenché lorsqu'un utilisateur clique sur un bouton. Les différents boutons ont des identifiants d'action différents. Deux identifiants sont prédéfinis : `close` et `openURL`, les autres sont personnalisés et peuvent être définis dans le builder. | | **didSelectProduct** | optionnel | Déclenché lorsqu'un produit est sélectionné pour l'achat (par l'utilisateur ou par le système). | | **didStartPurchase** | optionnel | Déclenché lorsque l'utilisateur commence le processus d'achat. | | **didFinishPurchase** | optionnel | Déclenché lorsqu'un achat se termine avec succès. | | **didFinishWebPaymentNavigation** | optionnel | Déclenché après une tentative d'ouverture d'un [paywall web](web-paywall) pour un achat, qu'elle ait réussi ou échoué. | | **didStartRestore** | optionnel | Déclenché lorsque l'utilisateur lance le processus de restauration. | | **didFailLoadingProducts** | optionnel | Déclenché lorsque des erreurs surviennent pendant le chargement des produits. Retournez `true` pour relancer le chargement. | | **didPartiallyLoadProducts** | optionnel | Déclenché lorsque les produits sont partiellement chargés. | | **showAlertItem** | optionnel | Un binding qui gère l'affichage des éléments d'alerte au-dessus du paywall. | | **showAlertBuilder** | optionnel | Une fonction pour afficher la vue d'alerte. | | **placeholderBuilder** | optionnel | Une fonction pour afficher la vue de remplacement pendant le chargement du paywall. | ## Gestion des événements dans UIKit \{#handling-events-in-uikit\} Pour contrôler ou surveiller les processus qui se déroulent sur l'écran du paywall dans votre application mobile, implémentez les méthodes `AdaptyPaywallControllerDelegate`. ### Événements générés par l'utilisateur \{#user-generated-events\} #### Sélection d'un produit \{#product-selection\} Lorsqu'un utilisateur sélectionne un produit pour l'acheter, cette méthode est appelée : ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer ) { } ```
Exemple d'événement (cliquer pour agrandir) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
#### Achat démarré \{#started-purchase\} Si un utilisateur initie le processus d'achat, cette méthode sera invoquée : ```swift showLineNumbers title="Swift" func paywallController(_ controller: AdaptyPaywallController, didStartPurchase product: AdaptyPaywallProduct) { } ```
Exemple d'événement (cliquez pour développer) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
Cette fonction ne sera pas appelée en mode Observer. Consultez la rubrique [iOS - Présenter les paywalls Paywall Builder en mode Observer](ios-present-paywall-builder-paywalls-in-observer-mode) pour plus de détails. #### Achat démarré via un paywall web \{#started-purchase-using-a-web-paywall\} Si un utilisateur initie le processus d'achat via un [paywall web](web-paywall), cette méthode sera invoquée : ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct ) { } ```
Exemple d'événement (Cliquez pour développer) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
#### Achat réussi ou annulé \{#successful-or-canceled-purchase\} Si l'achat réussit, cette méthode sera appelée : ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer, purchaseResult: AdaptyPurchaseResult ) { } } ```
Exemples d'événements (Cliquez pour développer) ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Cancelled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "cancelled" } } ```
Nous recommandons de fermer le paywall dans ce cas. Cette méthode ne sera pas invoquée en mode Observer. Consultez la rubrique [iOS - Présenter les paywalls du Paywall Builder en mode Observer](ios-present-paywall-builder-paywalls-in-observer-mode) pour plus de détails. #### Achat échoué \{#failed-purchase\} Si un achat échoue en raison d'une erreur, cette méthode est appelée. Cela inclut les erreurs StoreKit (restrictions de paiement, produits invalides, échecs réseau), les échecs de vérification de transaction et les erreurs système. Notez que les annulations utilisateur déclenchent `didFinishPurchase` avec un résultat annulé, et les paiements en attente ne déclenchent pas cette méthode. ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError ) { } ```
Exemple d'événement (Cliquez pour développer) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ```
Elle ne sera pas invoquée en mode Observer. Consultez la rubrique [iOS - Présenter des paywalls Paywall Builder en mode Observer](ios-present-paywall-builder-paywalls-in-observer-mode) pour plus de détails. #### Échec d'un achat via un paywall web \{#failed-purchase-using-a-web-paywall\} Si `Adapty.openWebPaywall()` échoue, cette méthode sera invoquée : ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFailWebPaymentNavigation product: AdaptyPaywallProduct, error: AdaptyError ) { } ```
Exemple d'événement (cliquez pour développer) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "web_payment_failed", "message": "Web payment navigation failed", "details": { "underlyingError": "Network connection error" } } } ```
#### Restauration réussie \{#successful-restore\} Si la restauration d'un achat réussit, cette méthode sera invoquée : ```swift showLineNumbers title="Swift" func paywallController( _ controller: AdaptyPaywallController, didFinishRestoreWith profile: AdaptyProfile ) { } ```
Exemple d'événement (Cliquez pour développer) ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ```
Nous recommandons de fermer l'écran si l'utilisateur dispose du `accessLevel` requis. Consultez la rubrique [Statut de l'abonnement](subscription-status) pour savoir comment le vérifier. #### Échec de la restauration \{#failed-restore\} Si la restauration d'un achat échoue, cette méthode sera appelée : ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailRestoreWith error: AdaptyError ) { } ```
Exemple d'événement (Cliquez pour développer) ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ```
### Récupération et rendu des données \{#data-fetching-and-rendering\} #### Erreurs de chargement des produits \{#product-loading-errors\} Si vous ne transmettez pas le tableau de produits lors de l'initialisation, AdaptyUI récupérera lui-même les objets nécessaires depuis le serveur. Si cette opération échoue, AdaptyUI signalera l'erreur en appelant cette méthode : ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailLoadingProductsWith error: AdaptyError ) -> Bool { return true } ```
Exemple d'événement (Cliquez pour développer) ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ```
Si vous renvoyez `true`, AdaptyUI répétera la requête après 2 secondes. #### Erreurs de rendu \{#rendering-errors\} Si une erreur survient lors du rendu de l'interface, elle sera signalée par cette méthode : ```swift showLineNumbers title="Swift" public func paywallController( _ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError ) { } ```
Exemple d'événement (cliquez pour développer) ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ```
Dans une situation normale, de telles erreurs ne devraient pas se produire. Si vous en rencontrez une, veuillez nous en informer.
--- # File: ios-use-fallback-paywalls --- --- title: "iOS - Utiliser les fallbacks" description: "Gérez les cas où les utilisateurs sont hors ligne ou les serveurs Adapty ne sont pas disponibles" --- To maintain a fluid user experience, it is important to set up [fallbacks](/fallback-paywalls) for your flows, [paywalls](paywalls), and [onboardings](onboardings). This precaution extends the application's capabilities in case of partial or complete loss of internet connection. * **If the application cannot access Adapty servers:** It will be able to display a fallback flow or paywall, and access the local onboarding configuration. * **If the application cannot access the internet:** It will be able to display a fallback flow or paywall. Onboardings include remote content and require an internet connection to function. :::important Before you follow the steps in this guide, [download](/local-fallback-paywalls) the fallback configuration files from Adapty. ::: ## Configuration \{#configuration\} 1. Ajoutez le fichier JSON de secours à votre bundle de projet : ouvrez le menu **File** dans XCode et sélectionnez l'option **Add Files to "YourProjectName"**. 2. Appelez la méthode `.setFallback` **avant** de récupérer le flow, le paywall ou l'onboarding cible. ```swift showLineNumbers do { if let urlPath = Bundle.main.url(forResource: fileName, withExtension: "json") { try await Adapty.setFallback(fileURL: urlPath) } } catch { // handle the error } ``` ```swift showLineNumbers if let url = Bundle.main.url(forResource: "ios_fallback", withExtension: "json") { Adapty.setFallback(fileURL: url) } ``` Paramètres : | Paramètre | Description | | :---------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fileURL** | Chemin vers le fichier de configuration de secours. | --- # File: localizations-and-locale-codes --- --- title: "Utiliser les localisations et codes de langue dans le SDK iOS" description: "Gérez les localisations et codes de langue de votre app pour toucher un public mondial dans votre app iOS." --- ## Pourquoi c'est important \{#why-this-is-important\} Les codes de langue entrent en jeu lorsqu'Adapty choisit la localisation d'un flow ou d'un onboarding, et lorsque vous lisez un Remote Config pour un paywall personnalisé. Les codes de langue sont complexes et peuvent varier d'une plateforme à l'autre. C'est pourquoi Adapty s'appuie sur un standard interne unique pour toutes les plateformes qu'il prend en charge. Comprendre ce standard vous aide à anticiper quelle localisation reçoit un utilisateur. ## Standard des codes de langue chez Adapty \{#locale-code-standard-at-adapty\} Pour les codes de langue, Adapty utilise une version légèrement modifiée du [standard BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) : chaque code est composé de sous-balises en minuscules, séparées par des tirets. Quelques exemples : `en` (anglais), `pt-br` (portugais (Brésil)), `zh` (chinois simplifié), `zh-hant` (chinois traditionnel). ## Correspondance des codes de langue \{#locale-code-matching\} Dans le SDK v4, les flows et les onboardings correspondent aux codes de langue différemment : les flows sont localisés par le SDK sur l'appareil, les onboardings par le serveur Adapty. ### Flows et paywalls du Paywall Builder Un paywall construit dans le Paywall Builder est livré sous forme de flow dans le SDK v4, donc la règle ci-dessous couvre les deux. La correspondance est exacte. Le SDK compare le code que vous transmettez avec les codes de localisation du flow caractère par caractère : il ne modifie pas la casse, ne remplace pas les underscores (`_`) par des tirets (`-`), et ne revient pas au sous-tag de langue. Pour un flow avec une localisation `pt-br`, seul `pt-br` correspond : `pt-BR`, `pt_BR` et `pt-PT` ne correspondent pas. Lorsque le code ne correspond à aucune localisation, le flow s'affiche silencieusement dans sa [langue par défaut](add-paywall-locale-in-adapty-paywall-builder#set-the-default-locale) — le SDK ne renvoie pas d'erreur et n'enregistre pas d'avertissement. Lorsque le code correspond, Adapty fusionne la localisation avec celle par défaut : les chaînes et les assets que la localisation correspondante ne définit pas sont repris de la localisation par défaut. Omettre le code de langue ne revient pas à demander la localisation par défaut du flow : le SDK substitue un `en` fixe. Un flow dont la langue par défaut est `de` s'affichera quand même en `en` s'il dispose d'une localisation `en`, et ne reviendra au `de` qu'en l'absence de celle-ci. :::warning Transmettez le code de langue exactement tel qu'il est configuré dans le tableau de bord — sous-balises en minuscules séparées par des tirets. Ne transmettez pas directement un identifiant de locale système : `Locale.current.identifier` renvoie `pt_BR` et `Locale.current.identifier(.bcp47)` renvoie `pt-BR`, et les deux basculent sur la localisation par défaut. Convertissez la valeur dans votre application avant de la transmettre. ::: ### Onboardings Les onboardings sont localisés côté serveur, et le serveur accepte d'autres formats. Lorsque vous passez un `locale` à [`getOnboarding`](get-onboardings) : 1. La chaîne de locale est convertie en minuscules et tous les underscores (`_`) sont remplacés par des tirets (`-`) 2. Adapty recherche la localisation dont le code de locale correspond exactement 3. Si aucune correspondance n'est trouvée, Adapty extrait la sous-chaîne avant le premier tiret (`pt` pour `pt-br`) et recherche la localisation correspondante 4. Si aucune correspondance n'est trouvée non plus, Adapty renvoie le contenu dans la locale par défaut de l'onboarding Cette approche permet à `pt_BR`, `pt-BR` et `pt-br` de pointer vers la même localisation d'onboarding. ## Implémenter les localisations \{#implementing-localizations\} Dans le SDK v4, vous ne passez pas de code de langue lors de la récupération d'un flow — `getFlow` renvoie le flow avec l'ensemble de ses localisations. - **Flows créés dans le builder** : le SDK ne lit pas la locale de l'appareil, vous devez donc la résoudre dans votre application et la passer à `AdaptyUI.getFlowConfiguration(forFlow:locale:)`. Ce paramètre est optionnel — omettez-le et le flow s'affiche en `en`, ou dans sa [locale par défaut](add-paywall-locale-in-adapty-paywall-builder#set-the-default-locale) si le flow n'a pas de localisation `en`. - **Paywalls personnalisés (Remote Config)** : `getFlow` renvoie toutes les localisations configurées dans `flow.remoteConfigs`. Chaque entrée contient un code `locale` et le contenu de la configuration (`jsonString`, ou le `dictionary` analysé). Sélectionnez l'entrée qui correspond à l'utilisateur, avec votre propre logique de secours : ```swift showLineNumbers do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") let config = flow.remoteConfigs.first(where: { $0.locale == "en" }) ?? flow.remoteConfigs.first // read your values from config?.dictionary } catch { // handle the error } ``` Adapty stocke ces codes `locale` dans le format décrit dans [Standard des codes de locale dans Adapty](#locale-code-standard-at-adapty). Le SDK ne fait pas correspondre les Remote Configs à une locale, c'est donc votre application qui décide quelle entrée appliquer. ## Pourquoi c'est important \{#why-this-is-important\} Il existe plusieurs situations où les codes de langue entrent en jeu — par exemple, lorsque vous essayez de récupérer le bon paywall pour la localisation actuelle de votre application. Les codes de langue étant complexes et pouvant varier d'une plateforme à l'autre, nous nous appuyons sur un standard interne pour toutes les plateformes que nous supportons. Cependant, en raison de cette complexité, il est vraiment important de comprendre exactement ce que vous envoyez à notre serveur pour obtenir la bonne localisation, et ce qui se passe ensuite — afin que vous receviez toujours ce que vous attendez. ## Standard des codes de langue chez Adapty \{#locale-code-standard-at-adapty\} Pour les codes de langue, Adapty utilise une version légèrement modifiée du [standard BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) : chaque code est composé de sous-balises en minuscules, séparées par des tirets. Quelques exemples : `en` (anglais), `pt-br` (portugais (Brésil)), `zh` (chinois simplifié), `zh-hant` (chinois traditionnel). ## Correspondance des codes de langue \{#locale-code-matching\} Lorsqu'Adapty reçoit un appel du SDK avec le code de langue et commence à chercher la localisation correspondante d'un paywall, voici ce qui se passe : 1. La chaîne de locale reçue est convertie en minuscules et tous les underscores (`_`) sont remplacés par des tirets (`-`) 2. On recherche ensuite la localisation dont le code de locale correspond exactement 3. Si aucune correspondance n'est trouvée, on extrait la sous-chaîne avant le premier tiret (`pt` pour `pt-br`) et on cherche la localisation correspondante 4. Si aucune correspondance n'est trouvée non plus, on renvoie le contenu dans la locale par défaut du paywall Ainsi, un appareil iOS qui a envoyé `'pt_BR'`, un appareil Android qui a envoyé `pt-BR`, et un autre appareil qui a envoyé `pt-br` obtiendront le même résultat. ## Implémentation des localisations : méthode recommandée \{#implementing-localizations-recommended-way\} Si vous vous posez des questions sur les localisations, il y a de grandes chances que vous travailliez déjà avec des fichiers de chaînes localisées dans votre projet. Dans ce cas, nous recommandons d'ajouter une paire clé-valeur avec le code de locale Adapty souhaité dans chacun de vos fichiers pour les localisations correspondantes. Extrayez ensuite la valeur de cette clé lors de l'appel à notre SDK, comme ceci : ```swift showLineNumbers // 1. Modify your Localizable.strings files /* Localizable.strings - Spanish */ adapty_paywalls_locale = "es"; /* Localizable.strings - Portuguese (Brazil) */ adapty_paywalls_locale = "pt-br"; // 2. Extract and use the locale code let locale = NSLocalizedString("adapty_paywalls_locale", comment: "") // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` Ainsi, vous gardez un contrôle total sur la localisation qui sera récupérée pour chaque utilisateur de votre application. ## Implémenter les localisations : l'autre façon \{#implementing-localizations-the-other-way\} Vous pouvez obtenir des résultats similaires (mais pas identiques) sans définir explicitement des codes de langue pour chaque localisation. Cela consiste à extraire un code de langue depuis d'autres objets fournis par votre plateforme, comme ceci : ```swift showLineNumbers let locale = Locale.current.identifier // pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method ``` Notez que nous ne recommandons pas cette approche pour plusieurs raisons : 1. Sur iOS, les langues préférées et la locale actuelle ne sont pas identiques. Pour que la localisation soit correctement sélectionnée, vous devrez soit vous appuyer sur la logique d'Apple, qui fonctionne automatiquement si vous utilisez l'approche recommandée avec des fichiers de chaînes localisées, soit la recréer vous-même. 2. Il est difficile de prédire ce que le serveur d'Adapty recevra exactement. Par exemple, sur iOS, il est possible d'obtenir une locale comme `ar_OM@numbers='latn'` sur un appareil et de l'envoyer à notre serveur. Dans ce cas, vous obtiendrez non pas la localisation `ar-om` que vous recherchiez, mais plutôt `ar`, ce qui est probablement inattendu. Should you decide to use this approach anyway — make sure you've covered all the relevant use cases. --- # File: ios-troubleshoot-paywall-builder --- --- title: "Troubleshoot Paywall Builder in iOS SDK" description: "Troubleshoot Paywall Builder in iOS SDK" --- Ce guide vous aide à résoudre les problèmes courants lors de l'utilisation de paywalls conçus dans le Paywall Builder d'Adapty avec le SDK iOS. ## La récupération de la configuration du paywall échoue \{#getting-a-paywall-configuration-fails\} **Problème** : La méthode `getPaywallConfiguration` ne parvient pas à récupérer la configuration du paywall. **Raison** : Le paywall n'est pas activé pour l'affichage sur l'appareil dans le Paywall Builder. **Solution** : Activez le bouton **Show on device** dans le Paywall Builder. ## Le nombre de vues du paywall est trop élevé \{#the-paywall-view-number-is-too-big\} **Problème** : Le nombre de vues du paywall affiche le double du chiffre attendu. **Raison** : Vous appelez peut-être `logShowFlow` (SDK iOS v4+) / `logShowPaywall` dans votre code, ce qui duplique le compteur de vues si vous utilisez le Paywall Builder ou le Flow Builder. Pour les flows et les paywalls créés avec ces outils, les analytics sont suivis automatiquement — vous n'avez donc pas besoin d'utiliser cette méthode. **Solution** : Vérifiez que vous n'appelez pas `logShowFlow` (SDK iOS v4+) / `logShowPaywall` dans votre code si vous utilisez le Paywall Builder ou le Flow Builder. ## Autres problèmes \{#other-issues\} **Problème** : Vous rencontrez d'autres problèmes liés au Paywall Builder qui ne sont pas abordés ci-dessus. **Solution** : Mettez à jour le SDK vers la dernière version à l'aide des [guides de migration](ios-sdk-migration-guides) si nécessaire. De nombreux problèmes sont résolus dans les versions récentes du SDK. --- # File: ios-present-paywall-builder-paywalls-in-observer-mode --- --- title: "Afficher les paywalls Paywall Builder en mode Observer dans le SDK iOS" description: "Apprenez à afficher les paywalls PB en mode observer pour de meilleures informations." --- Si vous avez personnalisé un paywall avec le Paywall Builder, vous n'avez pas à vous soucier de son rendu dans le code de votre application mobile pour l'afficher à l'utilisateur. Un tel paywall contient à la fois ce qui doit être affiché et comment il doit l'être. :::warning Cette section concerne uniquement le [mode Observer](observer-vs-full-mode). Si vous ne travaillez pas en mode Observer, consultez [iOS - Afficher les paywalls Paywall Builder](ios-present-paywalls). :::
Avant de commencer à afficher des flows (cliquez pour développer) 1. Configurez l'intégration initiale d'Adapty [avec l'App Store](initial_ios). 2. Installez et configurez le SDK Adapty. Assurez-vous de définir le paramètre `observerMode` sur `true`. Consultez le [guide d'installation du SDK iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk). 3. [Créez des produits](create-product) dans l'Adapty Dashboard. 4. [Configurez des flows ou des paywalls dans les builders](create-paywall) et assignez-leur des produits. 5. [Créez des placements et assignez-leur vos flows ou paywalls](create-placement). 6. [Récupérez les flows et leur configuration](get-pb-paywalls) dans le code de votre application mobile.

1. Implémentez l'objet `AdaptyObserverModeResolver`. Le protocole est identique à celui du SDK v3 — le mode observer lui-même ne change pas entre le rendu des flows et des paywalls : ```swift showLineNumbers title="Swift" func observerMode(didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // call onStartPurchase / onFinishPurchase to notify AdaptyUI about the purchase progress } func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void, onFinishRestore: @escaping () -> Void) { // call onStartRestore / onFinishRestore to notify AdaptyUI about the restore progress } ``` 2. Créez un objet de configuration de flow en passant votre resolver comme paramètre `observerModeResolver:` : ```swift showLineNumbers title="Swift" do { let flowConfiguration = try await AdaptyUI.getFlowConfiguration( forFlow: flow, observerModeResolver: ) } catch { // handle the error } ``` Paramètres de la requête : | Paramètre | Présence | Description | | :----------------------- | :-------- | :----------------------------------------------------------------------------------------------------------------------- | | **forFlow** | requis | Un objet `AdaptyFlow` obtenu via `Adapty.getFlow(placementId:)`. Voir [Obtenir les flows et paywalls](get-pb-paywalls). | | **observerModeResolver** | requis | L'`AdaptyObserverModeResolver` que vous avez implémenté ci-dessus. | 3. Initialisez le contrôleur de flow avec `AdaptyUI.flowController(with:delegate:)` : ```swift showLineNumbers title="Swift" import AdaptyUI let visualFlow = try AdaptyUI.flowController( with: flowConfiguration, delegate: ) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :-------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | **flowConfiguration** | requis | Un objet `AdaptyUI.FlowConfiguration` contenant les détails visuels du flow. Voir [Obtenir les flows et paywalls](get-pb-paywalls). | | **delegate** | requis | Un `AdaptyFlowControllerDelegate` pour écouter les événements du flow. Voir [Gérer les événements de flow & paywall](ios-handling-events). | Retourne : | Objet | Description | | :------------------- | :----------------------------------------------------------- | | AdaptyFlowController | Un objet représentant l'écran de flow demandé. | 4. Affichez le contrôleur : ```swift showLineNumbers title="Swift" present(visualFlow, animated: true) ``` :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. ::: En SwiftUI, récupérez la configuration du flow avec le resolver et passez-la au modificateur `.flow` : ```swift showLineNumbers title="SwiftUI" @State var flowPresented = false @State var flowConfiguration: AdaptyUI.FlowConfiguration? var body: some View { Text("Hello, AdaptyUI!") .flow( isPresented: $flowPresented, flowConfiguration: flowConfiguration, didPerformAction: { action in switch action { case .close: flowPresented = false default: break } }, didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didReceiveError: { error in flowPresented = false } ) .task { flowConfiguration = try? await AdaptyUI.getFlowConfiguration( forFlow: flow, observerModeResolver: ) } } ``` Le paramètre `observerModeResolver:` sur `getFlowConfiguration` est ce qui permet au flow rendu de respecter votre logique d'achat personnalisée — le modificateur lui-même utilise les mêmes callbacks qu'en mode complet. :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. :::
Avant de commencer à afficher des paywalls (cliquez pour développer) 1. Configurez l'intégration initiale d'Adapty [avec Google Play](initial-android) et [avec l'App Store](initial_ios). 2. Installez et configurez le SDK Adapty. Assurez-vous de définir le paramètre `observerMode` sur `true`. Consultez nos instructions spécifiques par framework pour [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk). 3. [Créez des produits](create-product) dans l'Adapty Dashboard. 4. [Configurez des paywalls, assignez-leur des produits](create-paywall) et personnalisez-les avec le Paywall Builder dans l'Adapty Dashboard. 5. [Créez des placements et assignez-leur vos paywalls](create-placement) dans l'Adapty Dashboard. 6. [Récupérez les paywalls Paywall Builder et leur configuration](get-pb-paywalls) dans le code de votre application mobile.

1. Implémentez l'objet `AdaptyObserverModeResolver` : ```swift showLineNumbers title="Swift" func observerMode(didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void, onFinishRestore: @escaping () -> Void) { // use the onStartRestore and onFinishRestore callbacks to notify AdaptyUI about the process of the restore } ``` L'événement `observerMode(didInitiatePurchase:onStartPurchase:onFinishPurchase:)` vous informe que l'utilisateur a initié un achat. Vous pouvez déclencher votre propre flow d'achat personnalisé en réponse à ce callback. L'événement `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)` vous informe que l'utilisateur a initié une restauration. Vous pouvez déclencher votre propre flow de restauration personnalisé en réponse à ce callback. N'oubliez pas non plus d'invoquer les callbacks suivants pour notifier AdaptyUI de l'avancement de l'achat ou de la restauration. Cela est nécessaire pour le bon comportement du paywall, notamment pour afficher le loader : | Callback | Description | | :----------------- | :----------------------------------------------------------------------------------------------- | | onStartPurchase() | Ce callback doit être invoqué pour notifier AdaptyUI que l'achat a démarré. | | onFinishPurchase() | Ce callback doit être invoqué pour notifier AdaptyUI que l'achat est terminé. | | onStartRestore() | Ce callback doit être invoqué pour notifier AdaptyUI que la restauration a démarré. | | onFinishRestore() | Ce callback doit être invoqué pour notifier AdaptyUI que la restauration est terminée. | 2. Créez un objet de configuration de paywall : ```swift showLineNumbers title="Swift" do { let paywallConfiguration = try AdaptyUI.getPaywallConfiguration( forPaywall: , observerModeResolver: ) } catch { // handle the error } ``` Paramètres de la requête : | Paramètre | Présence | Description | | :----------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall** | requis | Un objet `AdaptyPaywall` pour obtenir un contrôleur pour le paywall souhaité. | | **ObserverModeResolver** | requis | L'objet `AdaptyObserverModeResolver` que vous avez implémenté à l'étape précédente. | 3. Initialisez le paywall visuel que vous souhaitez afficher avec la méthode `.paywallController(for:products:viewConfiguration:delegate:)` : ```swift showLineNumbers title="Swift" import AdaptyUI let visualPaywall = AdaptyUI.paywallController( with: , delegate: ) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :---------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall Configuration** | requis | Un objet `AdaptyUI.PaywallConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **Delegate** | requis | Un `AdaptyPaywallControllerDelegate` pour écouter les événements du paywall. Consultez la rubrique [Gestion des événements paywall](ios-handling-events) pour plus de détails. | Retourne : | Objet | Description | | :---------------------- | :----------------------------------------------------------- | | AdaptyPaywallController | Un objet représentant l'écran de paywall demandé. | Une fois l'objet créé avec succès, vous pouvez l'afficher ainsi : ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. ::: Pour afficher le paywall visuel sur l'écran de l'appareil, utilisez le modificateur `.paywall` en SwiftUI : ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywallConfiguration: , didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } ) } ``` Paramètres de la requête : | Paramètre | Présence | Description | | :------------------------ | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall Configuration** | requis | Un objet `AdaptyUI.PaywallConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **Products** | optionnel | Fournissez un tableau d'objets `AdaptyPaywallProduct` pour optimiser le moment d'affichage des produits à l'écran. Si `nil` est passé, AdaptyUI récupérera automatiquement les produits nécessaires. | | **TagResolver** | optionnel | Définissez un dictionnaire de tags personnalisés et leurs valeurs résolues. Les tags personnalisés servent de placeholders dans le contenu du paywall, remplacés dynamiquement par des chaînes spécifiques pour un contenu personnalisé. Consultez la rubrique Tags personnalisés dans le Paywall Builder pour plus de détails. | | **ObserverModeResolver** | optionnel | L'objet `AdaptyObserverModeResolver` que vous avez implémenté à l'étape précédente. | Paramètres de closure : | Paramètre de closure | Description | | :------------------- | :--------------------------------------------------------------------------------------------- | | **didFinishRestore** | Invoqué si `Adapty.restorePurchases()` réussit. | | **didFailRestore** | Invoqué si `Adapty.restorePurchases()` échoue. | | **didFailRendering** | Invoqué si une erreur survient lors du rendu de l'interface. | Consultez la rubrique [iOS - Gestion des événements](ios-handling-events) pour les autres paramètres de closure. :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. :::
Avant de commencer à afficher des paywalls (cliquez pour développer) 1. Configurez l'intégration initiale d'Adapty [avec Google Play](initial-android) et [avec l'App Store](initial_ios). 1. Installez et configurez le SDK Adapty. Assurez-vous de définir le paramètre `observerMode` sur `true`. Consultez nos instructions spécifiques par framework pour [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) et [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk). 2. [Créez des produits](create-product) dans l'Adapty Dashboard. 3. [Configurez des paywalls, assignez-leur des produits](create-paywall) et personnalisez-les avec le Paywall Builder dans l'Adapty Dashboard. 4. [Créez des placements et assignez-leur vos paywalls](create-placement) dans l'Adapty Dashboard. 5. [Récupérez les paywalls Paywall Builder et leur configuration](get-pb-paywalls) dans le code de votre application mobile.

1. Implémentez l'objet `AdaptyObserverModeDelegate` : ```swift showLineNumbers title="Swift" func paywallController(_ controller: AdaptyPaywallController, didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } ``` L'événement `paywallController(_:didInitiatePurchase:onStartPurchase:onFinishPurchase:)` vous informe que l'utilisateur a initié un achat. Vous pouvez déclencher votre propre flow d'achat personnalisé en réponse à cet événement. N'oubliez pas non plus d'invoquer les callbacks suivants pour notifier AdaptyUI de l'avancement de l'achat. Cela est nécessaire pour le bon comportement du paywall, notamment pour afficher le loader : | Callback | Description | | :--------------- | :--------------------------------------------------------------------------------------------- | | onStartPurchase | Ce callback doit être invoqué pour notifier AdaptyUI que l'achat a démarré. | | onFinishPurchase | Ce callback doit être invoqué pour notifier AdaptyUI que l'achat est terminé. | 2. Initialisez le paywall visuel que vous souhaitez afficher avec la méthode `.paywallController(for:products:viewConfiguration:delegate:observerModeDelegate:)` : ```swift showLineNumbers title="Swift" import AdaptyUI let visualPaywall = AdaptyUI.paywallController( for: , products: , viewConfiguration: , delegate: observerModeDelegate: ) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :----------------------- | :-------- | :----------------------------------------------------------- | | **Paywall** | requis | Un objet `AdaptyPaywall` pour obtenir un contrôleur pour le paywall souhaité. | | **Products** | optionnel | Fournissez un tableau d'objets `AdaptyPaywallProduct` pour optimiser le moment d'affichage des produits à l'écran. Si `nil` est passé, AdaptyUI récupérera automatiquement les produits nécessaires. | | **ViewConfiguration** | requis | Un objet `AdaptyUI.LocalizedViewConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **Delegate** | requis | Un `AdaptyPaywallControllerDelegate` pour écouter les événements du paywall. Consultez la rubrique [Gestion des événements paywall](ios-handling-events) pour plus de détails. | | **ObserverModeDelegate** | requis | L'objet `AdaptyObserverModeDelegate` que vous avez implémenté à l'étape précédente. | | **TagResolver** | optionnel | Définissez un dictionnaire de tags personnalisés et leurs valeurs résolues. Les tags personnalisés servent de placeholders dans le contenu du paywall, remplacés dynamiquement par des chaînes spécifiques pour un contenu personnalisé. Consultez la rubrique Tags personnalisés dans le Paywall Builder pour plus de détails. | Retourne : | Objet | Description | | :---------------------- | :----------------------------------------------------------- | | AdaptyPaywallController | Un objet représentant l'écran de paywall demandé. | Une fois l'objet créé avec succès, vous pouvez l'afficher ainsi : ```swift showLineNumbers title="Swift" present(visualPaywall, animated: true) ``` :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. ::: Pour afficher le paywall visuel sur l'écran de l'appareil, utilisez le modificateur `.paywall` en SwiftUI : ```swift showLineNumbers title="SwiftUI" @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, paywall: , configuration: , didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false }, observerModeDidInitiatePurchase: { product, onStartPurchase, onFinishPurchase in // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase }, ) } ``` Paramètres de la requête : | Paramètre | Présence | Description | | :---------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Paywall** | requis | Un objet `AdaptyPaywall` pour obtenir un contrôleur pour le paywall souhaité. | | **Product** | optionnel | Fournissez un tableau d'objets `AdaptyPaywallProduct` pour optimiser le moment d'affichage des produits à l'écran. Si `nil` est passé, AdaptyUI récupérera automatiquement les produits nécessaires. | | **Configuration** | requis | Un objet `AdaptyUI.LocalizedViewConfiguration` contenant les détails visuels du paywall. Utilisez la méthode `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consultez la rubrique [Récupérer les paywalls Paywall Builder et leur configuration](get-pb-paywalls) pour plus de détails. | | **TagResolver** | optionnel | Définissez un dictionnaire de tags personnalisés et leurs valeurs résolues. Les tags personnalisés servent de placeholders dans le contenu du paywall, remplacés dynamiquement par des chaînes spécifiques pour un contenu personnalisé. Consultez la rubrique Tags personnalisés dans le Paywall Builder pour plus de détails. | Paramètres de closure : | Paramètre de closure | Description | | :---------------------------------- | :--------------------------------------------------------------------------------------------- | | **didFinishRestore** | Invoqué si `Adapty.restorePurchases()` réussit. | | **didFailRestore** | Invoqué si `Adapty.restorePurchases()` échoue. | | **didFailRendering** | Invoqué si une erreur survient lors du rendu de l'interface. | | **observerModeDidInitiatePurchase** | Invoqué lorsqu'un utilisateur initie un achat. | Consultez la rubrique [iOS - Gestion des événements](ios-handling-events) pour les autres paramètres de closure. :::warning N'oubliez pas d'[associer les paywalls aux transactions d'achat](report-transactions-observer-mode). Sinon, Adapty ne pourra pas déterminer le paywall source de l'achat. :::
--- # File: ios-implement-paywalls-manually --- --- title: "Implémenter des paywalls manuellement dans le SDK iOS" description: "Découvrez comment implémenter des paywalls manuellement dans votre application iOS avec le SDK Adapty." --- ## Accepter les achats \{#accept-purchases\} Si vous travaillez avec des paywalls que vous avez implémentés vous-même, vous pouvez déléguer la gestion des achats à Adapty via la méthode `makePurchase`. Adapty se charge alors de tous les scénarios utilisateur, et vous n'avez plus qu'à gérer les résultats de l'achat. :::important `makePurchase` fonctionne avec les produits créés dans l'Adapty Dashboard. Assurez-vous de configurer les produits et les moyens de les récupérer dans le tableau de bord en suivant le [guide de démarrage rapide](quickstart). ::: ## Mode observateur \{#observer-mode\} Si vous souhaitez implémenter votre propre logique de gestion des achats de A à Z, tout en profitant des analyses avancées d'Adapty, vous pouvez utiliser le mode observateur. :::important Consultez les limitations du mode observateur [ici](observer-vs-full-mode). ::: --- # File: ios-quickstart-manual --- --- title: "Activer les achats dans votre paywall personnalisé sur iOS SDK" description: "Intégrez le SDK Adapty dans vos paywalls iOS personnalisés pour activer les achats intégrés." --- Ce guide décrit comment intégrer Adapty dans vos paywalls personnalisés. Gardez un contrôle total sur l'implémentation du paywall, tandis que le SDK Adapty récupère les produits, gère les nouveaux achats et restaure les précédents. :::important **Ce guide s'adresse aux développeurs qui implémentent des paywalls personnalisés.** Si vous souhaitez la solution la plus simple pour activer les achats, utilisez [Adapty Flow Builder](ios-quickstart-paywalls). Avec Flow Builder, vous créez des flows dans un éditeur visuel sans code, Adapty gère automatiquement toute la logique d'achat, et vous pouvez tester différents designs sans republier votre application. ::: ## Avant de commencer \{#before-you-start\} ### Configurer les produits \{#set-up-products\} Pour activer les achats intégrés, vous devez comprendre trois concepts clés : - [**Produits**](product) – tout ce que les utilisateurs peuvent acheter (abonnements, consommables, accès à vie) - [**Paywalls**](paywalls) – des configurations qui définissent quels produits proposer. Dans Adapty, les paywalls sont le seul moyen de récupérer des produits, mais cette conception vous permet de modifier les produits, les prix et les offres sans toucher au code de votre application. - [**Placements**](placements) – où et quand vous affichez les paywalls dans votre application (comme `main`, `onboarding`, `settings`). Vous configurez les paywalls pour les placements dans le tableau de bord, puis vous les demandez par ID de placement dans votre code. Cela facilite l'exécution de tests A/B et l'affichage de différents paywalls à différents utilisateurs. Assurez-vous de comprendre ces concepts même si vous travaillez avec votre paywall personnalisé. En gros, ils représentent simplement votre façon de gérer les produits que vous vendez dans votre application. Pour implémenter votre paywall personnalisé, vous devrez créer un **paywall** et l'ajouter à un **placement**. Cette configuration vous permet de récupérer vos produits. Pour comprendre ce que vous devez faire dans le tableau de bord, suivez le guide de démarrage rapide [ici](quickstart). ### Gérer les utilisateurs \{#manage-users\} Vous pouvez travailler avec ou sans authentification backend de votre côté. Cependant, le SDK Adapty gère différemment les utilisateurs anonymes et identifiés. Lisez le [guide de démarrage rapide sur l'identification](ios-quickstart-identify) pour comprendre les spécificités et vous assurer que vous gérez correctement les utilisateurs. ## Étape 1. Récupérer les produits \{#step-1-get-products\} Pour récupérer les produits de votre paywall personnalisé, vous devez : 1. Obtenir l'objet `flow` en passant l'ID de [placement](placements) à la méthode `getFlow`. 2. Obtenir le tableau de produits pour ce flow à l'aide de la méthode `getPaywallProducts`. ```swift func loadPaywall() async { do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") let products = try await Adapty.getPaywallProducts(flow: flow) // Use products to build your custom paywall UI } catch { // Handle the error } } ``` ```swift func loadPaywall() { Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): Adapty.getPaywallProducts(flow: flow) { result in switch result { case let .success(products): // Use products to build your custom paywall UI case let .failure(error): // Handle the error } } case let .failure(error): // Handle the error } } } ``` ## Étape 2. Accepter les achats \{#step-2-accept-purchases\} Lorsqu'un utilisateur appuie sur un produit dans votre paywall personnalisé, appelez la méthode `makePurchase` avec le produit sélectionné. Cela gérera le flux d'achat et retournera le profil mis à jour. ```swift func purchaseProduct(_ product: AdaptyPaywallProduct) async { do { let purchaseResult = try await Adapty.makePurchase(product: product) switch purchaseResult { case .userCancelled: // User canceled the purchase break case .pending: // Purchase is pending (e.g., awaiting parental approval) break case let .success(profile, transaction): // Purchase successful, profile updated break } } catch { // Handle the error } } ``` ```swift func purchaseProduct(_ product: AdaptyPaywallProduct) { Adapty.makePurchase(product: product) { result in switch result { case let .success(purchaseResult): switch purchaseResult { case .userCancelled: // User canceled the purchase break case .pending: // Purchase is pending (e.g., awaiting parental approval) break case let .success(profile, transaction): // Purchase successful, profile updated break } case let .failure(error): // Handle the error } } } ``` ## Étape 3. Restaurer les achats \{#step-3-restore-purchases\} Apple exige que toutes les applications avec des abonnements proposent un moyen aux utilisateurs de restaurer leurs achats. Bien que les achats soient automatiquement restaurés lorsqu'un utilisateur se connecte avec son identifiant Apple, vous devez quand même implémenter un bouton de restauration dans votre application. Appelez la méthode `restorePurchases` lorsque l'utilisateur appuie sur le bouton de restauration. Cela synchronisera son historique d'achats avec Adapty et retournera le profil mis à jour. ```swift func restorePurchases() async { do { let profile = try await Adapty.restorePurchases() // Restore successful, profile updated } catch { // Handle the error } } ``` ```swift func restorePurchases() { Adapty.restorePurchases { result in switch result { case let .success(profile): // Restore successful, profile updated case let .failure(error): // Handle the error } } } ``` ## Étapes suivantes \{#next-steps\} :::tip Des questions ou des problèmes ? Consultez notre [forum d'assistance](https://adapty.featurebase.app/) où vous trouverez des réponses aux questions fréquentes ou pourrez poser les vôtres. Notre équipe et notre communauté sont là pour vous aider ! ::: Votre paywall est prêt à être affiché dans l'application. [Testez vos achats en mode sandbox](test-purchases-in-sandbox) pour vous assurer de pouvoir effectuer un achat test depuis le paywall. Ensuite, [vérifiez si les utilisateurs ont finalisé leur achat](ios-check-subscription-status) pour déterminer s'il faut afficher le paywall ou accorder l'accès aux fonctionnalités payantes. --- # File: fetch-paywalls-and-products --- --- title: "Récupérer les paywalls et produits pour les paywalls de configuration distante dans le SDK iOS" description: "Récupérez les paywalls et produits dans le SDK iOS Adapty pour améliorer la monétisation des utilisateurs." --- Avant d'afficher les Remote Configs et les paywalls personnalisés, vous devez récupérer leurs informations. Notez que cette rubrique concerne les Remote Configs et les paywalls personnalisés. Pour récupérer des flows ou des paywalls personnalisés dans le **Flow Builder** ou le **Paywall Builder**, consultez les [iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls), et [Unity](unity-get-pb-paywalls). :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. :::
Avant de commencer à récupérer les flows et les produits dans votre application mobile (cliquez pour développer) 1. [Créez vos produits](create-product) dans l'Adapty Dashboard. 2. [Créez un flow ou un paywall et incorporez-y les produits](create-paywall) dans l'Adapty Dashboard. 3. [Créez des placements et incorporez votre flow ou paywall dans le placement](create-placement) dans l'Adapty Dashboard. 4. [Installez le SDK Adapty](sdk-installation-ios) dans votre application mobile.
## Récupérer les informations d'un flow \{#fetch-flow-information\} Dans Adapty, un [produit](product) est une combinaison de produits provenant de l'App Store et de Google Play. Ces produits multiplateformes sont intégrés dans des flows et des paywalls, ce qui vous permet de les présenter dans des placements spécifiques de votre application mobile. Pour afficher les produits, vous devez obtenir un `AdaptyFlow` depuis l'un de vos [placements](placements) à l'aide de la méthode `getFlow`. :::important **Ne codez pas en dur les identifiants de produit.** Le seul identifiant à coder en dur est l'identifiant du placement. Les flows sont configurés à distance, donc le nombre de produits et les offres disponibles peuvent changer à tout moment. Votre application doit gérer ces changements dynamiquement — si un flow retourne deux produits aujourd'hui et trois demain, affichez-les tous sans modifier le code. ::: ```swift showLineNumbers do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") // the requested flow } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): // the requested flow case let .failure(error): // handle the error } } ``` | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez spécifiée lors de la création d'un placement dans votre Adapty Dashboard. | | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et retourne les données en cache en cas d'échec. Nous recommandons cette option, car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs sont souvent confrontés à une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour retourner les données en cache si elles existent. Dans ce cas, les utilisateurs pourraient ne pas obtenir les toutes dernières données, mais ils bénéficieront de temps de chargement plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser en cours de session pour éviter les requêtes réseau.

Notez que le cache reste intact après le redémarrage de l'application et n'est effacé que lors de la désinstallation ou via un nettoyage manuel.

Le SDK Adapty stocke les flows et les paywalls sur deux couches : le cache mis à jour régulièrement décrit ci-dessus et les [paywalls de secours](fallback-paywalls). Nous utilisons également un CDN pour récupérer les flows et les paywalls plus rapidement, ainsi qu'un serveur de secours indépendant en cas d'inaccessibilité du CDN.

| | **loadTimeout** | par défaut : 5 sec |

Cette valeur limite le délai d'attente de cette méthode. Si le délai est dépassé, les données en cache ou le fallback local sont retournés.

Notez que dans de rares cas, cette méthode peut dépasser légèrement le délai spécifié dans `loadTimeout`, car l'opération peut impliquer différentes requêtes en arrière-plan.

| :::note Dans la v4, le paramètre `locale` a été déplacé hors de `getFlow` et placé dans `getFlowConfiguration` (utilisé uniquement lors du rendu avec AdaptyUI). Pour les paywalls personnalisés, toutes les locales disponibles sont retournées ensemble dans `flow.remoteConfigs` — choisissez celle qui correspond à la langue de l'appareil de l'utilisateur ou au paramètre de votre application. ::: N'encodez pas en dur les identifiants de produits ! Puisque les flows sont configurés à distance, les produits disponibles, leur nombre et les offres spéciales (comme les essais gratuits) peuvent changer avec le temps. Assurez-vous que votre code gère ces scénarios. Par exemple, si vous récupérez initialement 2 produits, votre application doit afficher ces 2 produits. Mais si vous en récupérez ensuite 3, votre application doit tous les afficher sans nécessiter de modification du code. La seule chose à encoder en dur est l'identifiant du placement. Paramètres de réponse : | Paramètre | Description | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | Un objet `AdaptyFlow` contenant le placement, les identifiants (`id`, `variationId`), le nom, un tableau `remoteConfigs` (une entrée par locale configurée) et un indicateur `hasViewConfiguration`. Pour récupérer les produits du flow, appelez `getPaywallProducts(flow:)`. | ## Récupérer les produits \{#fetch-products\} Une fois que vous avez le flow, vous pouvez interroger le tableau de produits qui lui correspond : ```swift showLineNumbers do { let products = try await Adapty.getPaywallProducts(flow: flow) // the requested products array } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywallProducts(flow: flow) { result in switch result { case let .success(products): // the requested products array case let .failure(error): // handle the error } } ``` Paramètres de réponse : | Paramètre | Description | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | Liste d'objets [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) contenant : l'identifiant du produit, le nom du produit, le prix, la devise, la durée de l'abonnement, et plusieurs autres propriétés. | Lors de l'implémentation de votre propre design de paywall, vous aurez probablement besoin d'accéder à ces propriétés de l'objet [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct). Les propriétés les plus couramment utilisées sont illustrées ci-dessous, mais consultez le document lié pour tous les détails sur l'ensemble des propriétés disponibles. | Propriété | Description | |------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Pour afficher le titre du produit, utilisez `product.localizedTitle`. La localisation est basée sur le pays du store sélectionné par l'utilisateur, et non sur la locale de l'appareil lui-même. | | **Price** | Pour afficher une version localisée du prix, utilisez `product.localizedPrice`. Cette localisation est basée sur les informations de locale de l'appareil. Vous pouvez également accéder au prix sous forme de nombre avec `product.price`. La valeur sera fournie dans la devise locale. Pour obtenir le symbole de devise associé, utilisez `product.currencySymbol`. | | **Subscription Period** | Pour afficher la période (ex. semaine, mois, année, etc.), utilisez `product.localizedSubscriptionPeriod`. Cette localisation est basée sur la locale de l'appareil. Pour récupérer la période d'abonnement par programmation, utilisez `product.subscriptionPeriod`. De là, vous pouvez accéder à l'enum `unit` pour obtenir la durée (c.-à-d. jour, semaine, mois, année ou inconnu). La valeur `numberOfUnits` vous donnera le nombre d'unités de période. Par exemple, pour un abonnement trimestriel, vous verrez `.month` dans la propriété unit et `3` dans la propriété numberOfUnits. | | **Introductory Offer** | Pour afficher un badge ou tout autre indicateur signalant qu'un abonnement contient une offre de lancement, consultez la propriété `product.subscriptionOffer`. Cet objet contient les propriétés suivantes :
• `offerType` : un enum avec les valeurs `introductory`, `promotional` et `winBack`. Les essais gratuits et les abonnements à prix réduit initiaux seront de type `introductory`.
• `price` : le prix réduit sous forme de nombre. Pour les essais gratuits, attendez-vous à voir `0` ici.
• `localizedPrice` : un prix formaté de la remise selon la locale de l'utilisateur.
• `localizedNumberOfPeriods` : une chaîne localisée selon la locale de l'appareil décrivant la durée de l'offre. Par exemple, une offre d'essai de trois jours affiche `3 days` dans ce champ.
• `subscriptionPeriod` : vous pouvez également obtenir les détails individuels de la période d'offre avec cette propriété. Elle fonctionne de la même manière pour les offres que ce qui est décrit dans la section précédente.
• `localizedSubscriptionPeriod` : une période d'abonnement formatée de la remise selon la locale de l'utilisateur. | :::note Dans la v4, tous les produits retournés par `getPaywallProducts(flow:)` incluent déjà les informations d'éligibilité aux offres. L'appel séparé `getPaywallProductsWithoutDeterminingOffer` de la v3 a été supprimé. ::: ## Accélérer la récupération des flows avec un flow d'audience par défaut \{#speed-up-flow-fetching-with-default-audience-flow\} En général, les flows sont récupérés presque instantanément, vous n'avez donc pas à vous en préoccuper. Cependant, si vous avez de nombreuses audiences et placements, et que vos utilisateurs disposent d'une connexion internet faible, la récupération d'un flow peut prendre plus de temps que souhaité. Dans ce cas, vous pouvez afficher un flow par défaut pour garantir une expérience utilisateur fluide plutôt que de ne rien afficher. Pour y remédier, vous pouvez utiliser la méthode `getFlowForDefaultAudience`, qui récupère le flow du placement spécifié pour l'audience **All Users**. Il est cependant essentiel de comprendre que l'approche recommandée est de récupérer le flow via la méthode `getFlow`, comme décrit dans la section [Récupérer les informations du flow](fetch-paywalls-and-products#fetch-flow-information) ci-dessus. :::warning Pourquoi nous recommandons d'utiliser `getFlow` La méthode `getFlowForDefaultAudience` présente quelques inconvénients majeurs : - **Problèmes potentiels de compatibilité ascendante** : Si vous avez besoin d'afficher des flows différents selon les versions de l'application (actuelle et future), vous risquez de rencontrer des difficultés. Vous devrez soit concevoir des flows compatibles avec la version actuelle (ancienne), soit accepter que les utilisateurs de cette version puissent rencontrer des problèmes avec des flows non rendus. - **Perte de ciblage** : Tous les utilisateurs verront le même flow conçu pour l'audience **All Users**, ce qui signifie que vous perdez le ciblage personnalisé (notamment basé sur les pays, l'attribution marketing ou vos propres attributs personnalisés). Si vous acceptez ces inconvénients pour bénéficier d'une récupération plus rapide des flows, utilisez la méthode `getFlowForDefaultAudience` comme suit. Sinon, continuez à utiliser `getFlow` décrit [ci-dessus](fetch-paywalls-and-products#fetch-flow-information). ::: ```swift showLineNumbers do { let flow = try await Adapty.getFlowForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") // the requested flow } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getFlowForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): // the requested flow case let .failure(error): // handle the error } } ``` | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez spécifiée lors de la création d'un placement dans votre Adapty Dashboard. | | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option, car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Cependant, si vous pensez que vos utilisateurs font face à une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs ne disposeront peut-être pas des toutes dernières données, mais bénéficieront de temps de chargement plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter des requêtes réseau.

Notez que le cache reste intact lors du redémarrage de l'application et n'est effacé que lors de la désinstallation de l'application ou via un nettoyage manuel.

|
Avant d'afficher un Remote Config ou des paywalls personnalisés, vous devez récupérer les informations correspondantes. Notez que cette rubrique porte sur les Remote Configs et les paywalls personnalisés. Pour savoir comment récupérer des paywalls créés avec le Paywall Builder, consultez les guides pour [iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls), et [Unity](unity-get-pb-paywalls). :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. :::
Avant de commencer à récupérer les paywalls et les produits dans votre application mobile (cliquez pour développer) 1. [Créez vos produits](create-product) dans l'Adapty Dashboard. 2. [Créez un paywall et intégrez les produits dans votre paywall](create-paywall) dans l'Adapty Dashboard. 3. [Créez des placements et intégrez votre paywall dans le placement](create-placement) dans l'Adapty Dashboard. 4. [Installez le SDK Adapty](sdk-installation-ios) dans votre application mobile.
## Récupérer les informations d'un paywall \{#fetch-paywall-information\} Dans Adapty, un [produit](product) est une combinaison de produits provenant à la fois de l'App Store et de Google Play. Ces produits multiplateforme sont intégrés dans des paywalls, ce qui vous permet de les afficher dans des placements spécifiques de votre application mobile. Pour afficher les produits, vous devez obtenir un [Paywall](paywalls) depuis l'un de vos [placements](placements) avec la méthode `getPaywall`. :::important **Ne codez pas les identifiants de produits en dur.** Le seul identifiant à coder en dur est l'identifiant de placement. Les paywalls sont configurés à distance, donc le nombre de produits et les offres disponibles peuvent changer à tout moment. Votre application doit gérer ces changements dynamiquement — si un paywall retourne deux produits aujourd'hui et trois demain, affichez-les tous sans modifier le code. ::: ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez spécifiée lors de la création d'un placement dans votre Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la [localisation du paywall](add-remote-config-locale). Ce paramètre doit être un code de langue composé d'un ou plusieurs sous-tags séparés par le caractère moins (**-**). Le premier sous-tag correspond à la langue, le second à la région.

Exemple : `en` désigne l'anglais, `pt-br` représente le portugais brésilien.

Consultez [Localisations et codes de langue](localizations-and-locale-codes) pour plus d'informations sur les codes de langue et la façon dont nous recommandons de les utiliser.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK essaie de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option, car elle garantit que vos utilisateurs obtiennent toujours les données les plus récentes.

Cependant, si vous pensez que vos utilisateurs ont une connexion internet instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs pourraient ne pas obtenir les toutes dernières données, mais ils bénéficieront de temps de chargement plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser en cours de session pour éviter les requêtes réseau.

Notez que le cache reste intact après un redémarrage de l'application et n'est effacé que lors d'une réinstallation ou d'un nettoyage manuel.

Le SDK Adapty stocke les paywalls en deux couches : le cache régulièrement mis à jour décrit ci-dessus et les [paywalls de secours](fallback-paywalls). Nous utilisons également un CDN pour récupérer les paywalls plus rapidement, ainsi qu'un serveur de secours indépendant en cas d'inaccessibilité du CDN. Ce système est conçu pour garantir que vous obtenez toujours la dernière version de vos paywalls tout en assurant la fiabilité même lorsque la connexion internet est limitée.

| | **loadTimeout** | par défaut : 5 sec |

Cette valeur limite le délai d'attente de cette méthode. Si le délai est dépassé, les données en cache ou le fallback local seront renvoyés.

Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut comporter différentes requêtes en arrière-plan.

| N'encodez pas les IDs de produits en dur ! Puisque les paywalls sont configurés à distance, les produits disponibles, leur nombre et les offres spéciales (comme les essais gratuits) peuvent évoluer au fil du temps. Assurez-vous que votre code gère ces scénarios. Par exemple, si vous récupérez initialement 2 produits, votre application doit afficher ces 2 produits. Mais si vous en récupérez ensuite 3, votre application doit les afficher tous les 3 sans nécessiter de modification du code. La seule chose à encoder en dur est l'ID du placement. Paramètres de réponse : | Paramètre | Description | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | Un objet [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) contenant : une liste d'identifiants de produits, l'identifiant du paywall, le Remote Config, et plusieurs autres propriétés. | ## Récupérer les produits \{#fetch-products\} Une fois le paywall récupéré, vous pouvez interroger le tableau de produits qui lui correspond : ```swift showLineNumbers do { let products = try await Adapty.getPaywallProducts(paywall: paywall) // the requested products array } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywallProducts(paywall: paywall) { result in switch result { case let .success(products): // the requested products array case let .failure(error): // handle the error } } ``` Paramètres de réponse : | Paramètre | Description | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | Liste d'objets [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) contenant : l'identifiant du produit, le nom, le prix, la devise, la durée d'abonnement et plusieurs autres propriétés. | Lors de l'implémentation de votre propre design de paywall, vous aurez probablement besoin d'accéder à ces propriétés de l'objet [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct). Les propriétés les plus couramment utilisées sont illustrées ci-dessous, mais consultez le document lié pour obtenir tous les détails sur l'ensemble des propriétés disponibles. | Propriété | Description | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | Pour afficher le titre du produit, utilisez `product.localizedTitle`. La localisation est basée sur le pays du store sélectionné par l'utilisateur, et non sur la locale de l'appareil. | | **Price** | Pour afficher le prix dans une version localisée, utilisez `product.localizedPrice`. Cette localisation est basée sur les informations de locale de l'appareil. Vous pouvez également accéder au prix sous forme numérique via `product.price`. La valeur sera fournie dans la devise locale. Pour obtenir le symbole de devise associé, utilisez `product.currencySymbol`. | | **Subscription Period** | Pour afficher la période (ex. semaine, mois, année, etc.), utilisez `product.localizedSubscriptionPeriod`. Cette localisation est basée sur la locale de l'appareil. Pour récupérer la période d'abonnement par programmation, utilisez `product.subscriptionPeriod`. Vous pouvez ensuite accéder à l'enum `unit` pour obtenir la durée (c'est-à-dire jour, semaine, mois, année ou inconnu). La valeur `numberOfUnits` vous donnera le nombre d'unités de période. Par exemple, pour un abonnement trimestriel, vous verrez `.month` dans la propriété `unit` et `3` dans la propriété `numberOfUnits`. | | **Introductory Offer** | Pour afficher un badge ou tout autre indicateur qu'un abonnement contient une offre de lancement, consultez la propriété `product.subscriptionOffer`. Cet objet contient les propriétés suivantes :
• `offerType` : un enum avec les valeurs `introductory`, `promotional` et `winBack`. Les essais gratuits et les abonnements initialement remisés seront de type `introductory`.
• `price` : le prix remisé sous forme numérique. Pour les essais gratuits, la valeur sera `0`.
• `localizedPrice` : le prix de la remise formaté selon la locale de l'utilisateur.
• `localizedNumberOfPeriods` : une chaîne localisée selon la locale de l'appareil décrivant la durée de l'offre. Par exemple, une offre d'essai de trois jours affiche `3 days` dans ce champ.
• `subscriptionPeriod` : vous pouvez également obtenir les détails individuels de la période de l'offre avec cette propriété. Son fonctionnement est le même que celui décrit dans la section précédente.
• `localizedSubscriptionPeriod` : la période d'abonnement de la remise formatée selon la locale de l'utilisateur. | ## Vérifier l'éligibilité aux offres de lancement sur iOS \{#check-intro-offer-eligibility-on-ios\} Par défaut, la méthode `getPaywallProducts` vérifie l'éligibilité aux offres de lancement, promotionnelles et de reconquête. Si vous avez besoin d'afficher des produits avant que le SDK détermine l'éligibilité aux offres, utilisez plutôt la méthode `getPaywallProductsWithoutDeterminingOffer`. :::note Après avoir affiché les produits initiaux, pensez à appeler la méthode `getPaywallProducts` habituelle pour mettre à jour les produits avec les informations d'éligibilité aux offres correctes. ::: ```swift showLineNumbers do { let products = try await Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall) // the requested products array without subscriptionOffer } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall) { result in switch result { case let .success(products): // the requested products array without subscriptionOffer case let .failure(error): // handle the error } } ``` ## Accélérer la récupération des paywalls avec le paywall de l'audience par défaut \{#speed-up-paywall-fetching-with-default-audience-paywall\} En général, les paywalls sont récupérés presque instantanément, vous n'avez donc pas à vous soucier d'optimiser ce processus. Cependant, si vous avez de nombreuses audiences et paywalls et que vos utilisateurs ont une connexion internet faible, la récupération d'un paywall peut prendre plus de temps que souhaité. Dans ce cas, vous pouvez afficher un paywall par défaut pour garantir une expérience fluide plutôt que de ne rien afficher du tout. Pour résoudre ce problème, vous pouvez utiliser la méthode `getPaywallForDefaultAudience`, qui récupère le paywall du placement spécifié pour l'audience **All Users**. Il est cependant essentiel de comprendre que l'approche recommandée est de récupérer le paywall via la méthode `getPaywall`, comme décrit dans la section [Récupérer les informations du paywall](fetch-paywalls-and-products#fetch-paywall-information) ci-dessus. :::warning Pourquoi nous recommandons d'utiliser `getPaywall` La méthode `getPaywallForDefaultAudience` présente quelques inconvénients importants : - **Problèmes potentiels de compatibilité ascendante** : Si vous devez afficher des paywalls différents selon les versions de l'application (version actuelle et versions futures), vous risquez de rencontrer des difficultés. Vous devrez soit concevoir des paywalls compatibles avec la version actuelle (héritée), soit accepter que les utilisateurs de cette version puissent rencontrer des problèmes avec des paywalls non rendus. - **Perte de ciblage** : Tous les utilisateurs verront le même paywall conçu pour l'audience **All Users**, ce qui signifie que vous perdez le ciblage personnalisé (notamment basé sur les pays, l'attribution marketing ou vos propres attributs personnalisés). Si vous acceptez ces inconvénients pour bénéficier d'une récupération plus rapide des paywalls, utilisez la méthode `getPaywallForDefaultAudience` comme suit. Sinon, restez sur `getPaywall` décrit [ci-dessus](fetch-paywalls-and-products#fetch-paywall-information). ::: ```swift showLineNumbers do { let paywall = try await Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID") // the requested paywall } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` :::note La méthode `getPaywallForDefaultAudience` est disponible à partir de la version 2.11.2 du SDK iOS. ::: | Paramètre | Présence | Description | |---------|--------|-----------| | **placementId** | requis | L'identifiant du [Placement](placements). C'est la valeur que vous avez spécifiée lors de la création d'un placement dans votre Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la [localisation du paywall](add-remote-config-locale). Ce paramètre doit être un code de langue composé d'un ou plusieurs sous-tags séparés par le caractère moins (**-**). Le premier sous-tag correspond à la langue, le second à la région.

Exemple : `en` désigne l'anglais, `pt-br` représente le portugais brésilien.

Consultez [Localisations et codes de langue](localizations-and-locale-codes) pour plus d'informations sur les codes de langue et nos recommandations d'utilisation.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données mises en cache en cas d'échec. Nous recommandons cette option, car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Toutefois, si vous pensez que vos utilisateurs ont une connexion instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs risquent de ne pas obtenir les toutes dernières données, mais les temps de chargement seront plus rapides, quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé lors du redémarrage de l'application et n'est effacé qu'en cas de réinstallation ou de nettoyage manuel.

|
--- # File: present-remote-config-paywalls --- --- title: "Afficher un paywall configuré via Remote Config dans le SDK iOS" description: "Découvrez comment présenter des paywalls Remote Config dans Adapty pour personnaliser l'expérience utilisateur." --- Si vous avez personnalisé un paywall via Remote Config, vous devrez implémenter le rendu dans le code de votre application mobile pour l'afficher aux utilisateurs. Comme Remote Config offre une flexibilité adaptée à vos besoins, c'est vous qui décidez de ce qui est inclus et de l'apparence de votre vue paywall. Adapty fournit une méthode pour récupérer la configuration distante, vous laissant toute liberté pour présenter votre paywall personnalisé. N'oubliez pas de [vérifier si un utilisateur est éligible à une offre de lancement sur iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios) et d'adapter la vue paywall pour gérer le cas où il l'est. ## Récupérer le Remote Config du flow et l'afficher \{#get-flow-remote-config-and-present-it\} En v4, un flow contient une entrée `AdaptyRemoteConfig` par locale configurée dans le tableau `remoteConfigs`. Choisissez la locale correspondant à la préférence de l'utilisateur, puis lisez les valeurs dont vous avez besoin. ```swift showLineNumbers do { let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") let config = flow.remoteConfigs.first(where: { $0.locale == "en" }) ?? flow.remoteConfigs.first let headerText = config?.dictionary?["header_text"] as? String } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") { result in let flow = try? result.get() let config = flow?.remoteConfigs.first(where: { $0.locale == "en" }) ?? flow?.remoteConfigs.first let headerText = config?.dictionary?["header_text"] as? String } ``` À ce stade, une fois que vous avez récupéré toutes les valeurs nécessaires, il est temps de les assembler en une page visuellement attractive. Veillez à ce que le design s'adapte aux différentes tailles d'écran et orientations des téléphones mobiles, pour une expérience fluide et agréable sur tous les appareils. :::warning Pensez à [enregistrer l'événement d'affichage du paywall](present-remote-config-paywalls#track-paywall-view-events) comme décrit ci-dessous, afin qu'Adapty Analytics puisse collecter les données pour les entonnoirs et les tests A/B. ::: Une fois l'affichage du paywall terminé, passez à la mise en place du flow d'achat. Quand l'utilisateur effectue un achat, appelez simplement `.makePurchase()` avec le produit de votre flow. Pour en savoir plus sur la méthode `.makePurchase()`, consultez [Effectuer des achats](making-purchases). Nous recommandons de [créer un paywall de secours appelé fallback paywall](fallback-paywalls). Ce paywall de secours s'affiche à l'utilisateur en l'absence de connexion internet ou de cache disponible, garantissant une expérience fluide même dans ces situations. ## Suivre les événements d'affichage du paywall \{#track-paywall-view-events\} Adapty vous aide à mesurer les performances de vos paywalls. Bien que les données d'achat soient collectées automatiquement, l'enregistrement des vues de paywall nécessite votre intervention, car vous seul savez quand un utilisateur voit un paywall. Pour enregistrer un événement d'affichage de paywall, appelez simplement `.logShowFlow(flow)` — il sera alors pris en compte dans vos métriques de paywall dans les entonnoirs et les tests A/B. :::important Il n'est pas nécessaire d'appeler `.logShowFlow(flow)` si vous affichez des flows ou des paywalls rendus par le [Flow Builder](adapty-flow-builder) ou le [Paywall Builder](adapty-paywall-builder). Adapty suit les vues automatiquement dans ces cas. ::: ```swift showLineNumbers try await Adapty.logShowFlow(flow) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :-------- | :------- |:-----------------------------------------------------------------------------------------| | **flow** | obligatoire | Un objet `AdaptyFlow` obtenu via `Adapty.getFlow(placementId:)`. | Si vous avez personnalisé un paywall via Remote Config, vous devrez implémenter le rendu dans le code de votre application mobile pour l'afficher aux utilisateurs. Comme Remote Config offre une flexibilité adaptée à vos besoins, c'est vous qui décidez de ce qui est inclus et de l'apparence de votre vue paywall. Nous fournissons une méthode pour récupérer la configuration distante, vous laissant toute liberté pour présenter votre paywall personnalisé configuré via Remote Config. N'oubliez pas de [vérifier si un utilisateur est éligible à une offre de lancement sur iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios) et d'adapter la vue paywall pour gérer le cas où il l'est. ## Récupérer le Remote Config du paywall et l'afficher \{#get-paywall-remote-config-and-present-it\} Pour récupérer le Remote Config d'un paywall, accédez à la propriété `remoteConfig` et extrayez les valeurs nécessaires. ```swift showLineNumbers do { let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") let headerText = paywall.remoteConfig?.dictionary?["header_text"] as? String } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") { result in let paywall = try? result.get() let headerText = paywall?.remoteConfig?.dictionary?["header_text"] as? String } ``` À ce stade, une fois que vous avez récupéré toutes les valeurs nécessaires, il est temps de les assembler en une page visuellement attractive. Veillez à ce que le design s'adapte aux différentes tailles d'écran et orientations des téléphones mobiles, pour une expérience fluide et agréable sur tous les appareils. :::warning Pensez à [enregistrer l'événement d'affichage du paywall](present-remote-config-paywalls#track-paywall-view-events) comme décrit ci-dessous, afin qu'Adapty Analytics puisse collecter les données pour les entonnoirs et les tests A/B. ::: Une fois l'affichage du paywall terminé, passez à la mise en place du flow d'achat. Quand l'utilisateur effectue un achat, appelez simplement `.makePurchase()` avec le produit de votre paywall. Pour en savoir plus sur la méthode `.makePurchase()`, consultez [Effectuer des achats](making-purchases). Nous recommandons de [créer un paywall de secours appelé fallback paywall](fallback-paywalls). Ce paywall de secours s'affiche à l'utilisateur en l'absence de connexion internet ou de cache disponible, garantissant une expérience fluide même dans ces situations. ## Suivre les événements d'affichage du paywall \{#track-paywall-view-events\} Adapty vous aide à mesurer les performances de vos paywalls. Bien que les données d'achat soient collectées automatiquement, l'enregistrement des vues de paywall nécessite votre intervention, car vous seul savez quand un utilisateur voit un paywall. Pour enregistrer un événement d'affichage de paywall, appelez simplement `.logShowPaywall(paywall)` — il sera alors pris en compte dans vos métriques de paywall dans les entonnoirs et les tests A/B. :::important Il n'est pas nécessaire d'appeler `.logShowPaywall(paywall)` si vous affichez des paywalls créés dans le [Paywall Builder](adapty-paywall-builder). ::: ```swift showLineNumbers Adapty.logShowPaywall(paywall) ``` Paramètres de la requête : | Paramètre | Présence | Description | | :---------- | :------- |:-----------------------------------------------------------------------------------------| | **paywall** | obligatoire | Un objet [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall). | --- # File: making-purchases --- --- title: "Effectuer des achats dans une application mobile avec le SDK iOS" description: "Guide pour gérer les achats intégrés et les abonnements avec Adapty." --- Afficher des paywalls dans votre application mobile est une étape indispensable pour proposer aux utilisateurs l'accès à du contenu ou des services premium. Cependant, présenter ces paywalls suffit à gérer les achats uniquement si vous utilisez le [Paywall Builder](adapty-paywall-builder) pour personnaliser vos paywalls. Si vous n'utilisez pas le Paywall Builder, vous devez utiliser une méthode séparée appelée `.makePurchase()` pour finaliser un achat et débloquer le contenu souhaité. Cette méthode est la porte d'entrée permettant aux utilisateurs d'interagir avec les paywalls et de procéder à leurs transactions. Si votre paywall comporte une offre promotionnelle active pour le produit qu'un utilisateur souhaite acheter, Adapty l'appliquera automatiquement au moment de l'achat. :::warning Gardez à l'esprit que l'offre de lancement ne sera appliquée automatiquement que si vous utilisez des paywalls configurés avec le Paywall Builder. Dans les autres cas, vous devrez [vérifier l'éligibilité de l'utilisateur à une offre de lancement sur iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Ignorer cette étape peut entraîner le rejet de votre application lors de sa publication. De plus, cela pourrait conduire à facturer le plein tarif à des utilisateurs éligibles à une offre de lancement. ::: Assurez-vous d'avoir [effectué la configuration initiale](quickstart) sans sauter une seule étape. Sans cela, nous ne pouvons pas valider les achats. ## Effectuer un achat \{#make-purchase\} :::note **Vous utilisez le [Paywall Builder](adapty-paywall-builder) ?** Les achats sont traités automatiquement — vous pouvez ignorer cette étape. **Vous cherchez un guide pas à pas ?** Consultez le [guide de démarrage rapide](ios-implement-paywalls-manually) pour des instructions d'implémentation complètes avec tout le contexte nécessaire. ::: ```swift showLineNumbers do { let purchaseResult = try await Adapty.makePurchase(product: product) switch purchaseResult { case .userCancelled: // Handle the case where the user canceled the purchase case .pending: // Handle deferred purchases (e.g., the user will pay offline with cash) case let .success(profile, transaction): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // Grant access to the paid features } } } catch { // Handle the error } ``` ```swift showLineNumbers Adapty.makePurchase(product: product) { result in switch result { case let .success(purchaseResult): switch purchaseResult { case .userCancelled: // Handle the case where the user canceled the purchase case .pending: // Handle deferred purchases (e.g., the user will pay offline with cash) case let .success(profile, transaction): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // Grant access to the paid features } } case let .failure(error): // Handle the error } } ``` Paramètres de la requête : | Paramètre | Présence | Description | | :---------- | :------- | :-------------------------------------------------------------------------------------------------- | | **Product** | requis | Un objet [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) récupéré depuis le paywall. | Paramètres de la réponse : | Paramètre | Description | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** |

Si la requête a abouti, la réponse contient cet objet. Un objet [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) fournit des informations complètes sur les niveaux d'accès, les abonnements et les achats uniques d'un utilisateur au sein de l'application.

Vérifiez le statut du niveau d'accès pour déterminer si l'utilisateur dispose de l'accès requis à l'application.

| :::warning **Remarque :** si vous utilisez encore StoreKit d'Apple en version inférieure à v2.0 et le SDK Adapty en version inférieure à v2.9.0, vous devez fournir le [secret partagé de l'App Store Apple](app-store-connection-configuration#step-5-enter-app-store-shared-secret) à la place. Cette méthode est actuellement dépréciée par Apple. ::: ## Achats intégrés depuis l'App Store \{#in-app-purchases-from-the-app-store\} Lorsqu'un utilisateur initie un achat dans l'App Store et que la transaction est transmise à votre application, vous avez deux options : - **Traiter la transaction immédiatement :** Retournez `true` dans `shouldAddStorePayment`. L'écran d'achat Apple s'affichera immédiatement. - **Stocker l'objet produit pour un traitement ultérieur :** Retournez `false` dans `shouldAddStorePayment`, puis appelez `makePurchase` avec le produit stocké plus tard. Cela peut être utile si vous souhaitez afficher quelque chose de personnalisé à l'utilisateur avant de déclencher un achat. Voici le code complet : ```swift showLineNumbers title="Swift" final class YourAdaptyDelegateImplementation: AdaptyDelegate { nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool { // 1a. // Return `true` to continue the transaction in your app. The Apple purchase system screen will show automatically. // 1b. // Store the product object and return `false` to defer or cancel the transaction. false } // 2. Continue the deferred purchase later on by passing the product to `makePurchase` when the timing is appropriate func continueDeferredPurchase() async { let storedProduct: AdaptyDeferredProduct = // get the product object from 1b. do { try await Adapty.makePurchase(product: storedProduct) } catch { // handle the error } } } ``` ## Utiliser des codes de réduction sur iOS \{#redeem-offer-codes-in-ios\}
À propos des codes d'offre Les codes d'offre vous permettent d'accorder des réductions ou des périodes d'essai gratuites à des utilisateurs spécifiques. Contrairement aux offres classiques appliquées automatiquement, les codes d'offre sont distribués en dehors de l'application — par e-mail, réseaux sociaux ou supports imprimés. Les utilisateurs les activent en saisissant le code dans l'App Store, en suivant une URL de validation ou via une boîte de dialogue intégrée à l'application. Pour configurer des codes d'offre, ouvrez un abonnement dans App Store Connect et accédez à sa section **Offer Codes**. Vous pouvez créer [trois types](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de codes d'offre : - **Free** — l'abonnement est gratuit pendant une durée définie, puis le renouvellement suivant se fait au plein tarif. - **Pay as you go** — l'utilisateur paie un tarif réduit à chaque cycle de facturation pendant une durée définie, puis l'abonnement se renouvelle au plein tarif. - **Pay up front** — l'utilisateur paie un prix unique réduit pour toute la durée de l'offre, puis l'abonnement se renouvelle au plein tarif. Vous n'avez pas besoin d'ajouter les codes d'offre à Adapty. Apple marque chaque transaction pendant la période d'offre avec la catégorie du code d'offre. Cela inclut la première activation et tous les renouvellements à tarif réduit qui suivent. Adapty détecte ce marquage et enregistre chaque transaction avec la catégorie d'offre `offer_code`. Une fois la période d'offre terminée et l'abonnement renouvelé au plein tarif, le marquage disparaît. Vous pouvez filtrer les analyses par le type d'offre **Offer Code** dans l'[Adapty Dashboard](controls-filters-grouping-compare-proceeds). #### Résolution des écarts de revenus \{#revenue-discrepancy-troubleshooting\} Si vous constatez qu'une transaction avec code d'offre apparaît dans Adapty au prix plein du produit plutôt qu'au prix réduit de l'offre, vérifiez les points suivants dans App Store Connect : - Le code d'offre dispose bien d'une tarification correcte configurée pour toutes les régions où les utilisateurs peuvent l'activer. - Le prix de l'offre est défini pour le pays ou la région spécifique de l'utilisateur. Apple envoie le prix régional dans la transaction. Si aucun prix régional n'est configuré pour l'offre, Apple peut envoyer le prix plein du produit à la place. Vous pouvez filtrer et vérifier les transactions avec code d'offre dans l'[Adapty Dashboard](controls-filters-grouping-compare-proceeds) à l'aide des filtres de type d'offre **Offer Code** et **Offer Discount Type**. #### Anciens codes promo (obsolètes) \{#legacy-promo-codes-deprecated\} :::warning Apple a supprimé les codes promo pour les achats intégrés en mars 2026. Les codes d'offre les remplacent avec davantage de fonctionnalités : éligibilité configurable, dates d'expiration et jusqu'à 1 million de codes par trimestre. Si vous utilisiez auparavant des codes promo pour les achats intégrés, passez aux codes d'offre dans App Store Connect. ::: Les anciens codes promo (limités à 100 par application et par version) donnaient un accès gratuit à un abonnement. Contrairement aux codes d'offre, Apple n'incluait pas les informations de réduction dans les transactions avec code promo — il envoyait le prix plein du produit dans le reçu. En conséquence, Adapty enregistrait ces transactions au prix plein, ce qui entraînait des écarts de revenus entre les analyses Adapty et App Store Connect. Si vous constatez des transactions historiques au prix plein qui auraient dû être gratuites, elles proviennent probablement d'anciens codes promo. Ces codes étant désormais obsolètes, passez aux codes d'offre pour un suivi précis des revenus.
Pour afficher la feuille de saisie de code de réduction dans votre application : ```swift showLineNumbers Adapty.presentCodeRedemptionSheet() ``` :::danger D'après nos observations, la feuille de saisie de code de réduction peut ne pas fonctionner de manière fiable dans certaines applications. Nous recommandons de rediriger l'utilisateur directement vers l'App Store. Pour ce faire, vous devez ouvrir l'URL au format suivant : `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: --- # File: restore-purchase --- --- title: "Restaurer les achats dans une application mobile avec le SDK iOS" description: "Découvrez comment restaurer les achats dans Adapty pour garantir une expérience utilisateur fluide." --- La restauration des achats est une fonctionnalité qui permet aux utilisateurs de récupérer l'accès à du contenu précédemment acheté — abonnements ou achats intégrés — sans être facturés à nouveau. Cette fonctionnalité est particulièrement utile pour les utilisateurs qui ont désinstallé puis réinstallé l'application, ou qui ont changé d'appareil et souhaitent retrouver leur contenu acheté sans repayer. :::note Dans les paywalls créés avec le [Paywall Builder](adapty-paywall-builder), les achats sont restaurés automatiquement sans code supplémentaire de votre part. Si c'est votre cas, vous pouvez ignorer cette étape. ::: Pour restaurer un achat sans utiliser le [Paywall Builder](adapty-paywall-builder) pour personnaliser le paywall, appelez la méthode `.restorePurchases()` : ```swift showLineNumbers do { let profile = try await Adapty.restorePurchases() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // successful access restore } } catch { // handle the error } ``` ```swift showLineNumbers Adapty.restorePurchases { [weak self] result in switch result { case let .success(profile): if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // successful access restore } case let .failure(error): // handle the error } } ``` Paramètres de réponse : | Paramètre | Description | |---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** |

Un objet [`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile). Ce modèle contient des informations sur les niveaux d'accès, les abonnements et les achats uniques.

Vérifiez le **statut du niveau d'accès** pour déterminer si l'utilisateur a accès à l'application.

| :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: --- # File: ios-transaction-management --- --- title: "Gestion avancée des transactions dans le SDK iOS" description: "Terminez les transactions manuellement dans votre application iOS avec le SDK Adapty." --- :::note La gestion avancée des transactions est prise en charge dans le SDK iOS Adapty à partir de la version 3.12. ::: La gestion avancée des transactions dans Adapty vous donne plus de contrôle sur la façon dont les transactions sont gérées, vérifiées et finalisées. Elle introduit trois fonctionnalités optionnelles qui fonctionnent ensemble : | Fonctionnalité | Objectif | |-------------------------------------------------------------|----------| | [`appAccountToken`](#assign-appaccounttoken) | Lie les transactions Apple à votre identifiant utilisateur interne | | [`jwsTransaction`](#access-the-jws-representation) | Fournit le payload de transaction signé par Apple pour la validation | | [Finalisation manuelle](#control-transaction-finishing-behavior) | Vous permet de finaliser les transactions uniquement après confirmation du succès par votre backend | Ensemble, ces outils vous permettent de construire des flux de validation personnalisés robustes pendant qu'Adapty continue de synchroniser les transactions avec son backend. :::important La plupart des applications n'en ont pas besoin. Par défaut, Adapty valide et finalise automatiquement les transactions StoreKit. Utilisez ce guide uniquement si vous gérez votre propre validation côté backend ou si vous souhaitez contrôler entièrement le cycle de vie des achats. ::: ## Assigner `appAccountToken` \{#assign-appaccounttoken\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) est un **UUID** qui vous permet de lier les transactions de l'App Store à l'identité interne de vos utilisateurs. StoreKit associe ce token à chaque transaction, ce qui permet à votre backend de faire correspondre les données App Store à vos utilisateurs. Utilisez un UUID stable généré par utilisateur et réutilisez-le pour le même compte sur tous les appareils. Cela garantit que les achats et les notifications App Store restent correctement liés. Vous pouvez définir le token de deux façons — lors de l'activation du SDK ou lors de l'identification de l'utilisateur. :::important Vous devez toujours passer `appAccountToken` avec `customerUserId`. Si vous ne passez que le token, il ne sera pas inclus dans la transaction. ::: ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } // Or when identifying a user: do { try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) } catch { // handle the error } ``` ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: ) Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } // Or when identifying a user: Adapty.identify("YOUR_USER_ID", withAppAccountToken: ) { error in if let error { // handle the error } } ``` ## Accéder à la représentation JWS \{#access-the-jws-representation\} Lorsque vous effectuez un achat, le résultat inclut la transaction Apple au [format JWS Compact Serialization](https://developer.apple.com/documentation/storekit/verificationresult/jwsrepresentation-21vgo). Vous pouvez transmettre cette valeur à votre backend pour une validation ou une journalisation indépendante. ```swift let result = try await Adapty.makePurchase(product: paywallProduct) let jwsRepresentation = result.jwsTransaction ``` ## Contrôler le comportement de finalisation des transactions \{#control-transaction-finishing-behavior\} Par défaut, Adapty finalise automatiquement les transactions StoreKit après validation. Si vous devez différer la finalisation jusqu'à ce que votre backend confirme le succès, définissez le comportement de finalisation sur manuel. Dans ce mode : - Adapty valide quand même les achats et les synchronise avec son backend. - Les transactions restent non finalisées jusqu'à ce que vous appeliez explicitement `finish()`. ```swift var configBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_API_KEY") .with(transactionFinishBehavior: .manual) try await Adapty.activate(with: configBuilder.build()) ``` Lorsque vous utilisez la finalisation manuelle des transactions, vous devez implémenter la méthode delegate `onUnfinishedTransaction` pour gérer les transactions non finalisées : ```swift showLineNumbers title="Swift" extension YourApp: AdaptyDelegate { func onUnfinishedTransaction(_ transaction: AdaptyUnfinishedTransaction) async { // Perform your custom validation logic here // When ready, finish the transaction await transaction.finish() } } ``` Pour obtenir toutes les transactions non finalisées en cours, utilisez la méthode `getUnfinishedTransactions()` : ```swift let unfinishedTransactions = try await Adapty.getUnfinishedTransactions() ``` --- # File: implement-observer-mode --- --- title: "Implémenter le mode Observateur dans le SDK iOS" description: "Implémentez le mode observateur dans Adapty pour suivre les événements d'abonnement utilisateur dans le SDK iOS." --- Si vous avez déjà votre propre infrastructure d'achat et que vous n'êtes pas prêt à passer entièrement à Adapty, vous pouvez explorer le [mode Observateur](observer-vs-full-mode). Dans sa forme de base, le mode Observateur offre des analyses avancées et une intégration fluide avec les systèmes d'attribution et d'analytics. Si cela correspond à vos besoins, vous devez uniquement : 1. L'activer lors de la configuration du SDK Adapty en définissant le paramètre `observerMode` sur `true`. 2. [Signaler les transactions](report-transactions-observer-mode) depuis votre infrastructure d'achat existante à Adapty. Si vous avez également besoin des paywalls et des tests A/B, une configuration supplémentaire est requise, comme décrit ci-dessous. ## Configuration du mode Observateur \{#observer-mode-setup\} Activez le mode Observateur si vous gérez vous-même les achats et le statut des abonnements, et que vous utilisez Adapty pour envoyer les événements d'abonnement et les analytics. :::important En mode Observateur, le SDK Adapty ne fermera aucune transaction — assurez-vous donc de les gérer vous-même. ::: ```swift showLineNumbers @main struct YourApp: App { init() { // Configure Adapty SDK let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(observerMode: true) let config = configurationBuilder.build() // Activate Adapty SDK asynchronously Task { do { try await Adapty.activate(with: configurationBuilder) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } var body: some Scene { WindowGroup { // Your content view } } } } ``` ```swift showLineNumbers Task { do { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard .with(observerMode: true) let config = configurationBuilder.build() try await Adapty.activate(with: config) } catch { // Handle error appropriately for your app print("Adapty activation failed: ", error) } } ``` Paramètres : | Paramètre | Description | | --------------------------- | ------------------------------------------------------------ | | observerMode | Une valeur booléenne qui contrôle le [mode Observateur](observer-vs-full-mode). La valeur par défaut est `false`. | ## Utiliser les paywalls Adapty en mode Observateur \{#using-adapty-paywalls-in-observer-mode\} Si vous souhaitez également utiliser les paywalls et les fonctionnalités de test A/B d'Adapty, c'est possible — mais cela nécessite une configuration supplémentaire en mode Observateur. Voici ce que vous devrez faire en plus des étapes ci-dessus : 1. Affichez les paywalls normalement pour les [paywalls à Remote Config](present-remote-config-paywalls). Pour les paywalls Paywall Builder, suivez les guides de configuration spécifiques pour [iOS](ios-present-paywall-builder-paywalls-in-observer-mode). 3. [Associez les paywalls](report-transactions-observer-mode) aux transactions d'achat. --- # File: report-transactions-observer-mode --- --- title: "Déclarer les transactions en Observer Mode dans le SDK iOS" description: "Déclarez les transactions d'achat en Observer Mode d'Adapty pour obtenir des informations sur les utilisateurs et suivre les revenus dans le SDK iOS." --- En Observer Mode, le SDK Adapty ne peut pas suivre automatiquement les achats effectués via votre système d'achat existant. Vous devez déclarer les transactions depuis votre app store. Il est indispensable de mettre cela en place **avant** de publier votre application pour éviter des erreurs dans les analyses. Utilisez `reportTransaction` pour déclarer explicitement chaque transaction afin qu'Adapty puisse la reconnaître. :::warning **Ne sautez pas la déclaration des transactions !** Si vous n'appelez pas `reportTransaction`, Adapty ne reconnaîtra pas la transaction, elle n'apparaîtra pas dans les analyses et ne sera pas envoyée aux intégrations. ::: Si vous utilisez des paywalls Adapty, incluez le `variationId` lors de la déclaration d'une transaction. Cela relie l'achat au paywall qui l'a déclenché, garantissant ainsi des analyses de paywall précises. ```swift showLineNumbers do { // every time when calling transasction.finish() try await Adapty.reportTransaction(transaction, withVariationId: ) } catch { // handle the error } ``` Paramètres : | Paramètre | Présence | Description | | --------------- | ---------- | ------------------------------------------------------------ | | **transaction** | obligatoire |
  • Pour StoreKit 1 : SKPaymentTransaction.
  • Pour StoreKit 2 : Transaction.
| | **variationId** | optionnel | L'identifiant unique de la variante du paywall. Récupérez-le depuis la propriété `variationId` de l'objet [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
En Observer Mode, le SDK Adapty ne peut pas suivre automatiquement les achats effectués via votre système d'achat existant. Vous devez déclarer les transactions depuis votre app store ou les restaurer. Il est indispensable de mettre cela en place **avant** de publier votre application pour éviter des erreurs dans les analyses. Utilisez `reportTransaction` pour envoyer les données de transaction à Adapty. :::warning **Ne sautez pas la déclaration des transactions !** Si vous n'appelez pas `reportTransaction`, Adapty ne reconnaîtra pas la transaction, elle n'apparaîtra pas dans les analyses et ne sera pas envoyée aux intégrations. ::: Si vous utilisez des paywalls Adapty, incluez le `withVariationId` lors de la déclaration d'une transaction. Cela relie l'achat au paywall qui l'a déclenché, garantissant ainsi des analyses de paywall précises. ```swift showLineNumbers do { // every time when calling transasction.finish() try await Adapty.reportTransaction(transaction, withVariationId: ) } catch { // handle the error } ``` Paramètres : | Paramètre | Présence | Description | | --------------- | ---------- | ------------------------------------------------------------ | | **transaction** | obligatoire |
  • Pour StoreKit 1 : SKPaymentTransaction.
  • Pour StoreKit 2 : Transaction.
| | **variationId** | optionnel | L'identifiant unique de la variante du paywall. Récupérez-le depuis la propriété `variationId` de l'objet [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
**Déclaration des transactions** - Les versions jusqu'à 3.1.x écoutent automatiquement les transactions dans l'App Store, la déclaration manuelle n'est donc pas nécessaire. - La version 3.2 ne prend pas en charge l'Observer Mode. **Association des paywalls aux transactions** Le SDK Adapty ne peut pas déterminer la source des achats, car c'est vous qui les traitez. Par conséquent, si vous comptez utiliser des paywalls et/ou des tests A/B en Observer Mode, vous devez associer la transaction provenant de votre app store au paywall correspondant dans le code de votre application mobile. Il est important de le faire correctement avant de publier votre application, sinon cela entraînera des erreurs dans les analyses. ```swift let variationId = paywall.variationId // There are two overloads: for StoreKit 1 and StoreKit 2 Adapty.setVariationId(variationId, forPurchasedTransaction: transactionId) { error in if error == nil { // successful binding } } ``` Paramètres de la requête : | Paramètre | Présence | Description | | ------------- | ---------- | ------------------------------------------------------------ | | variationId | obligatoire | L'identifiant de chaîne de la variante. Vous pouvez l'obtenir via la propriété `variationId` de l'objet [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). | | transactionId | obligatoire |

Pour StoreKit 1 : un objet [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).

Pour StoreKit 2 : un objet [Transaction](https://developer.apple.com/documentation/storekit/transaction).

|
--- # File: ios-troubleshoot-purchases --- --- title: "Troubleshoot purchases in iOS SDK" description: "Troubleshoot purchases in iOS SDK" --- Ce guide vous aide à résoudre les problèmes courants lors de l'implémentation manuelle des achats dans le SDK iOS. ## AdaptyError.cantMakePayments en mode observer \{#adaptyerrorcantmakepayments-in-observer-mode\} **Problème** : Vous obtenez `AdaptyError.cantMakePayments` en utilisant `makePurchase` en mode observer. **Raison** : En mode observer, vous devez gérer les achats de votre côté, et non utiliser la méthode `makePurchase` d'Adapty. **Solution** : Si vous utilisez `makePurchase` pour les achats, désactivez le mode observer. Vous devez soit utiliser `makePurchase`, soit gérer les achats de votre côté en mode observer. Consultez [Implémenter le mode Observer](implement-observer-mode) pour plus de détails. ## makePurchasesCompletionHandlers introuvable \{#not-found-makepurchasescompletionhandlers\} **Problème** : Vous rencontrez des problèmes avec `makePurchasesCompletionHandlers` qui est introuvable. **Raison** : Cela est généralement lié à des problèmes de test en sandbox. **Solution** : Créez un nouvel utilisateur sandbox et réessayez. Cela résout souvent les problèmes de gestionnaire de complétion d'achat liés au sandbox. ## Autres problèmes \{#other-issues\} **Problème** : Vous rencontrez d'autres problèmes liés aux achats non couverts ci-dessus. **Solution** : Migrez le SDK vers la dernière version en utilisant les [guides de migration](ios-sdk-migration-guides) si nécessaire. De nombreux problèmes sont résolus dans les versions plus récentes du SDK. --- # File: ios-web-paywall --- --- title: "Implémenter des web paywalls dans le SDK iOS" description: "Configurez un web paywall pour accepter des paiements sans les frais ni les audits de l'App Store." --- :::important Avant de commencer, assurez-vous d'avoir [configuré votre web paywall dans le tableau de bord](web-paywall) et d'avoir installé le SDK Adapty version 3.6.1 ou ultérieure. ::: ## Ouvrir des web paywalls \{#open-web-paywalls\} Si vous travaillez avec un paywall que vous avez développé vous-même, vous devez gérer les web paywalls à l'aide de la méthode SDK. La méthode `.openWebPaywall` : 1. Génère une URL unique permettant à Adapty de relier un paywall spécifique affiché à un utilisateur particulier à la page web vers laquelle il est redirigé. 2. Détecte le retour de vos utilisateurs dans l'app, puis appelle `.getProfile` à intervalles courts pour vérifier si les droits d'accès du profil ont été mis à jour. Ainsi, si le paiement a réussi et que les droits d'accès ont été mis à jour, l'abonnement s'active dans l'app presque immédiatement. ```swift showLineNumbers title="Swift" do { try await Adapty.openWebPaywall(for: product) } catch { print("Failed to open web paywall: \(error)") } ``` :::note Il existe deux versions de la méthode `openWebPaywall` : 1. `openWebPaywall(product)` qui génère des URL par paywall et ajoute également les données du produit aux URL. 2. `openWebPaywall(paywall)` qui génère des URL par paywall sans ajouter les données du produit aux URL. À utiliser lorsque vos produits dans le paywall Adapty diffèrent de ceux du web paywall. ::: ## Gérer les erreurs \{#handle-errors\} | Erreur | Description | Action recommandée | |-----------------------------------------|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------| | AdaptyError.paywallWithoutPurchaseUrl | Le paywall n'a pas d'URL d'achat web configurée | Vérifiez si le paywall a été correctement configuré dans l'Adapty Dashboard | | AdaptyError.productWithoutPurchaseUrl | Le produit n'a pas d'URL d'achat web | Vérifiez la configuration du produit dans l'Adapty Dashboard | | AdaptyError.failedOpeningWebPaywallUrl | Impossible d'ouvrir l'URL dans le navigateur | Vérifiez les paramètres de l'appareil ou proposez une autre méthode d'achat | | AdaptyError.failedDecodingWebPaywallUrl | Échec de l'encodage des paramètres dans l'URL | Vérifiez que les paramètres de l'URL sont valides et correctement formatés | ## Exemple d'implémentation \{#implementation-example\} ```swift showLineNumbers title="Swift" class SubscriptionViewController: UIViewController { var paywall: AdaptyPaywall? @IBAction func purchaseButtonTapped(_ sender: UIButton) { guard let paywall = paywall, let product = paywall.products.first else { return } Task { await offerWebPurchase(for: product) } } func offerWebPurchase(for paywallProduct: AdaptyPaywallProduct) async { do { // Attempt to open web paywall try await Adapty.openWebPaywall(for: paywallProduct) } catch let error as AdaptyError { switch error { case .paywallWithoutPurchaseUrl, .productWithoutPurchaseUrl: showAlert(message: "Web purchase is not available for this product.") case .failedOpeningWebPaywallUrl: showAlert(message: "Could not open web browser. Please try again.") default: showAlert(message: "An error occurred: \(error.localizedDescription)") } } catch { showAlert(message: "An unexpected error occurred.") } } // Helper methods private func showAlert(message: String) { /* ... */ } } ``` :::note Lorsque les utilisateurs reviennent dans l'app, actualisez l'interface pour refléter les mises à jour du profil. `AdaptyDelegate` recevra et traitera les événements de mise à jour du profil. ::: ## Ouvrir des web paywalls dans un navigateur intégré \{#open-web-paywalls-in-an-in-app-browser\} :::important L'ouverture des web paywalls dans un navigateur intégré est prise en charge à partir du SDK Adapty v3.15. ::: Par défaut, les web paywalls s'ouvrent dans le navigateur externe. Pour offrir une expérience utilisateur fluide, vous pouvez ouvrir les web paywalls dans un navigateur intégré. La page d'achat web s'affiche alors directement dans votre application, permettant aux utilisateurs de finaliser leurs transactions sans quitter l'app. Pour activer cette option, définissez le paramètre `in` sur `.inAppBrowser` : ```swift showLineNumbers title="Swift" do { try await Adapty.openWebPaywall(for: product, in: .inAppBrowser) // default – .externalBrowser } catch { print("Failed to open web paywall: \(error)") } ``` --- # File: ios-user --- --- title: "Utilisateurs et accès dans le SDK iOS" description: "Apprenez à gérer les utilisateurs et les niveaux d'accès dans votre application iOS avec le SDK Adapty." --- --- # File: identifying-users --- --- title: "Identifier les utilisateurs dans le SDK iOS" description: "Identifiez les utilisateurs dans Adapty pour améliorer les expériences d'abonnement personnalisées." --- Adapty crée un ID de profil interne pour chaque utilisateur. Cependant, si vous disposez de votre propre système d'authentification, vous devez définir votre propre Customer User ID. Vous pouvez retrouver les utilisateurs par leur Customer User ID dans la section [Profiles](profiles-crm) et l'utiliser dans l'[API côté serveur](getting-started-with-server-side-api), qui sera transmis à toutes les intégrations. ## Définir le Customer User ID lors de la configuration \{#set-customer-user-id-on-configuration\} Si vous disposez d'un ID utilisateur au moment de la configuration, passez-le simplement comme paramètre `customerUserId` à la méthode `.activate()` : ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } ``` ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID") Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: ## Définir le Customer User ID après la configuration \{#set-customer-user-id-after-configuration\} Si vous ne disposez pas d'un ID utilisateur lors de la configuration du SDK, vous pouvez le définir plus tard à tout moment avec la méthode `.identify()`. Les cas les plus courants pour utiliser cette méthode sont après une inscription ou une connexion, lorsque l'utilisateur passe d'un utilisateur anonyme à un utilisateur authentifié. ```swift showLineNumbers do { try await Adapty.identify("YOUR_USER_ID") } catch { // handle the error } ``` ```swift showLineNumbers Adapty.identify("YOUR_USER_ID") { error in if let error { // handle the error } } ``` Paramètres de la requête : - **Customer User ID** (requis) : un identifiant utilisateur sous forme de chaîne de caractères. :::warning Resoumission des données utilisateur importantes Dans certains cas, par exemple lorsqu'un utilisateur se reconnecte à son compte, les serveurs d'Adapty disposent déjà d'informations sur cet utilisateur. Dans ces situations, le SDK Adapty basculera automatiquement vers le nouvel utilisateur. Si vous avez transmis des données à l'utilisateur anonyme, comme des attributs personnalisés ou des attributions provenant de réseaux tiers, vous devez resoumettre ces données pour l'utilisateur identifié. Il est également important de noter que vous devez redemander tous les paywalls et produits après avoir identifié l'utilisateur, car les données du nouvel utilisateur peuvent être différentes. ::: ## Déconnexion et reconnexion \{#logging-out-and-logging-in\} Vous pouvez déconnecter l'utilisateur à tout moment en appelant la méthode `.logout()` : ```swift showLineNumbers do { try await Adapty.logout() } catch { // handle the error } ``` ```swift showLineNumbers Adapty.logout { error in if error == nil { // successful logout } } ``` Vous pouvez ensuite reconnecter l'utilisateur avec la méthode `.identify()`. ## Définir l'appAccountToken \{#set-appaccounttoken\} L'[`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) est un UUID qui aide StoreKit 2 d'Apple à identifier les utilisateurs sur plusieurs installations et appareils. À partir du SDK Adapty iOS 3.10.2, vous pouvez passer l'`appAccountToken` lors de la configuration du SDK ou lors de l'identification d'un utilisateur : ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) do { try await Adapty.activate(with: configurationBuilder.build()) } catch { // handle the error } // Or when identifying a user: do { try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) } catch { // handle the error } ``` ```swift showLineNumbers // During configuration: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID()) Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } // Or when identifying a user: Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) { error in if let error { // handle the error } } ``` Vous pouvez ensuite reconnecter l'utilisateur avec la méthode `.identify()`. ## Détecter les utilisateurs sur plusieurs appareils \{#detect-users-across-devices\} Lors de l'activation du SDK, il lit automatiquement les droits existants de l'utilisateur depuis StoreKit (iOS) ou Google Play Billing (Android) et les synchronise avec le backend Adapty. Un abonnement actif apparaît sur le profil Adapty sans que l'application n'appelle `restorePurchases`. Ce qui **ne** se produit **pas** automatiquement, c'est la reconnaissance qu'un profil sur un nouvel appareil appartient au même utilisateur que le profil sur l'appareil d'origine. Adapty fait correspondre les profils par Customer User ID, donc la continuité d'identité dépend de ce que vous utilisez comme CUID. **Ce qu'Adapty peut détecter entre les appareils** | Votre configuration | Ce qu'Adapty détecte | Ce que vous devez faire | | --- | --- | --- | | Customer User ID = `device_id` (sans connexion à l'application) | Le nouvel appareil reçoit un CUID différent et donc un profil différent. L'abonnement se synchronise avec le nouveau profil via un événement **Access level updated**, mais `subscription_started` ne se déclenche pas — le nouveau profil est traité comme un héritier de l'achat d'origine. Les analyses basées sur `subscription_started` sous-compteront les utilisateurs de retour. | Utilisez un identifiant de compte stable comme Customer User ID pour qu'un utilisateur de retour corresponde au profil existant sur tous les appareils. | | Customer User ID = identifiant de compte stable (connexion sur chaque appareil) | Le SDK synchronise automatiquement l'abonnement lors de l'appel `activate()`, et `identify()` fait correspondre le profil existant par CUID. | Aucune configuration supplémentaire n'est nécessaire — l'identité et l'abonnement se résolvent automatiquement. | | Héritier du partage familial Apple | Le membre de la famille reçoit l'abonnement uniquement via un événement **Access level updated** — `subscription_started` ne se déclenche pas. | Écoutez **Access level updated**. Consultez [Apple Family Sharing](apple-family-sharing) pour la matrice complète des événements. | | Même compte Apple/Google, utilisateurs in-app différents | Le premier profil à enregistrer l'achat devient le parent. Les profils suivants voient l'abonnement via une chaîne d'héritiers, avec un seul événement **Access level updated**. | Exigez une connexion, puis choisissez un [mode de partage](sharing-paid-access-between-user-accounts) adapté à votre modèle. | **Restaurer les achats sur un nouvel appareil** Proposez un bouton « Restaurer les achats » initié par l'utilisateur sur votre paywall. Les directives App Review d'Apple (règle 3.1.1) l'exigent, et il sert de solution de secours quand la synchronisation automatique rate un cas limite. Ce bouton doit appeler `restorePurchases` dans votre SDK. Un appel programmatique à `restorePurchases` au premier lancement n'est pas nécessaire pour une utilisation normale — le SDK effectue déjà l'équivalent lors de l'appel `activate()`. Réservez les appels programmatiques pour forcer une vérification fraîche du reçu, par exemple lors du débogage d'un accès manquant après la fin de `activate()`. --- # File: setting-user-attributes --- --- title: "Définir les attributs utilisateur dans le SDK iOS" description: "Apprenez à définir des attributs utilisateur dans Adapty pour améliorer la segmentation des audiences." --- Vous pouvez définir des attributs optionnels comme l'e-mail, le numéro de téléphone, etc., pour les utilisateurs de votre application. Ces attributs peuvent ensuite être utilisés pour créer des [segments](segments) d'utilisateurs ou simplement les consulter dans le CRM. ### Définir les attributs utilisateur \{#setting-user-attributes\} Pour définir des attributs utilisateur, appelez la méthode `.updateProfile()` : ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(email: "email@email.com") .with(phoneNumber: "+18888888888") .with(firstName: "John") .with(lastName: "Appleseed") .with(gender: .other) .with(birthday: Date()) do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } ``` ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(email: "email@email.com") .with(phoneNumber: "+18888888888") .with(firstName: "John") .with(lastName: "Appleseed") .with(gender: .other) .with(birthday: Date()) Adapty.updateProfile(params: builder.build()) { error in if error != nil { // handle the error } } ``` Notez que les attributs définis précédemment via la méthode `updateProfile` ne seront pas réinitialisés. :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: ### Liste des clés autorisées \{#the-allowed-keys-list\} Les clés autorisées `` de `AdaptyProfileParameters.Builder` et les valeurs `` correspondantes sont listées ci-dessous : | Clé | Valeur | |---|-----| |

email

phoneNumber

firstName

lastName

| String | | gender | Enum, valeurs autorisées : `female`, `male`, `other` | | birthday | Date | ### Attributs utilisateur personnalisés \{#custom-user-attributes\} Vous pouvez définir vos propres attributs personnalisés, généralement liés à l'utilisation de votre application. Par exemple, pour une application de fitness, il peut s'agir du nombre d'exercices par semaine ; pour une application d'apprentissage des langues, du niveau de connaissance de l'utilisateur, etc. Vous pouvez les utiliser dans des segments pour créer des paywalls et des offres ciblées, et les exploiter dans vos analyses pour identifier quelles métriques produit influencent le plus les revenus. ```swift showLineNumbers do { builder = try builder.with(customAttribute: "value1", forKey: "key1") } catch { // handle key/value validation error } ``` Pour supprimer une clé existante, utilisez la méthode `.withRemoved(customAttributeForKey:)` : ```swift showLineNumbers do { builder = try builder.withRemoved(customAttributeForKey: "key2") } catch { // handle error } ``` Il peut arriver que vous ayez besoin de consulter les attributs personnalisés déjà définis. Pour cela, utilisez le champ `customAttributes` de l'objet `AdaptyProfile`. :::warning Gardez à l'esprit que la valeur de `customAttributes` peut ne pas être à jour, car les attributs utilisateur peuvent être envoyés depuis différents appareils à tout moment — les attributs côté serveur ont donc pu changer depuis la dernière synchronisation. ::: ### Limites \{#limits\} - Jusqu'à 30 attributs personnalisés par utilisateur - Les noms de clés peuvent comporter jusqu'à 30 caractères. Ils peuvent contenir des caractères alphanumériques ainsi que les caractères suivants : `_` `-` `.` - La valeur peut être une chaîne de caractères ou un nombre décimal, avec 50 caractères maximum. --- # File: subscription-status --- --- title: "Vérifier le statut d'abonnement dans le SDK iOS" description: "Suivez et gérez le statut d'abonnement des utilisateurs dans Adapty pour améliorer la rétention." --- Avec Adapty, suivre le statut d'abonnement est simple. Vous n'avez pas à insérer manuellement des identifiants de produits dans votre code. Il vous suffit de vérifier le statut d'abonnement d'un utilisateur en contrôlant l'existence d'un [niveau d'accès](access-level) actif. Avant de commencer à vérifier le statut d'abonnement, configurez les [notifications serveur App Store](enable-app-store-server-notifications). ## Niveau d'accès et objet AdaptyProfile \{#access-level-and-the-adaptyprofile-object\} Les niveaux d'accès sont des propriétés de l'objet [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). Nous recommandons de récupérer le profil au démarrage de votre application, par exemple lorsque vous [identifiez un utilisateur](identifying-users#set-customer-user-id-on-configuration), puis de le mettre à jour à chaque modification. Vous pouvez ainsi utiliser l'objet profil sans avoir à le redemander constamment. Pour être notifié des mises à jour du profil, écoutez les changements comme décrit dans la section [Écouter les mises à jour du statut d'abonnement](subscription-status#listening-for-subscription-status-updates) ci-dessous. :::tip Vous souhaitez voir un exemple concret d'intégration du SDK Adapty dans une application mobile ? Consultez nos [exemples d'applications](sample-apps), qui illustrent la configuration complète, notamment l'affichage des paywalls, les achats et d'autres fonctionnalités de base. ::: ## Récupérer le niveau d'accès depuis le serveur \{#retrieving-the-access-level-from-the-server\} Pour obtenir le niveau d'accès depuis le serveur, utilisez la méthode `.getProfile()` : ```swift showLineNumbers do { let profile = try await Adapty.getProfile() if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get() { // check the access profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false { // grant access to premium features } } } ``` Paramètres de la réponse : | Paramètre | Description | | --------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile |

Un objet [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). En général, il suffit de vérifier le statut du niveau d'accès du profil pour déterminer si l'utilisateur bénéficie d'un accès premium à l'application.

La méthode `.getProfile` fournit le résultat le plus à jour, car elle interroge toujours l'API. Si, pour une raison quelconque (par exemple, absence de connexion Internet), le SDK Adapty ne parvient pas à récupérer les informations depuis le serveur, les données en cache sont retournées. Il est également important de noter que le SDK Adapty met à jour le cache `AdaptyProfile` régulièrement afin de maintenir ces informations aussi à jour que possible.

| La méthode `.getProfile()` vous fournit le profil utilisateur depuis lequel vous pouvez obtenir le statut du niveau d'accès. Vous pouvez avoir plusieurs niveaux d'accès par application. Par exemple, si vous avez une application de journal et que vous vendez des abonnements à différents sujets indépendamment, vous pouvez créer des niveaux d'accès « sports » et « science ». Mais la plupart du temps, vous n'aurez besoin que d'un seul niveau d'accès ; dans ce cas, vous pouvez simplement utiliser le niveau d'accès par défaut « premium ». Voici un exemple pour vérifier le niveau d'accès « premium » par défaut : ```swift showLineNumbers do { let profile = try await Adapty.getProfile() let isPremium = profile.accessLevels["premium"]?.isActive ?? false // grant access to premium features } catch { // handle the error } ``` ```swift showLineNumbers Adapty.getProfile { result in if let profile = try? result.get(), profile.accessLevels["premium"]?.isActive ?? false { // grant access to premium features } } ``` ### Écouter les mises à jour du statut d'abonnement \{#listening-for-subscription-status-updates\} Chaque fois que l'abonnement d'un utilisateur change, Adapty déclenche un événement. Pour recevoir les messages d'Adapty, vous devez effectuer quelques configurations supplémentaires : ```swift showLineNumbers Adapty.delegate = self // To receive subscription updates, extend `AdaptyDelegate` with this method: nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { // handle any changes to subscription state } ``` Adapty déclenche également un événement au démarrage de l'application. Dans ce cas, le statut d'abonnement en cache est transmis. ### Cache du statut d'abonnement \{#subscription-status-cache\} Le cache implémenté dans le SDK Adapty stocke le statut d'abonnement du profil. Cela signifie que même si le serveur est indisponible, les données en cache restent accessibles pour fournir des informations sur le statut d'abonnement du profil. Il est toutefois important de noter qu'il n'est pas possible d'interroger directement le cache. Le SDK interroge périodiquement le serveur toutes les minutes pour vérifier les éventuelles mises à jour liées au profil. En cas de modifications — nouvelles transactions ou autres changements — elles sont transmises aux données en cache afin de les maintenir synchronisées avec le serveur. --- # File: ios-deal-with-att --- --- title: "Gérer l'ATT dans le SDK iOS" description: "Démarrez avec Adapty sur iOS pour simplifier la configuration et la gestion des abonnements." --- Si votre application utilise le framework AppTrackingTransparency et présente une demande d'autorisation de suivi à l'utilisateur, vous devez envoyer le [statut d'autorisation](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) à Adapty. ```swift showLineNumbers let builder = AdaptyProfileParameters.Builder() .with(appTrackingTransparencyStatus: .authorized) do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } ``` ```swift showLineNumbers if #available(iOS 14, macOS 11.0, *) { let builder = AdaptyProfileParameters.Builder() .with(appTrackingTransparencyStatus: .authorized) Adapty.updateProfile(params: builder.build()) { [weak self] error in if error != nil { // handle the error } } } ``` :::warning Nous vous recommandons vivement d'envoyer cette valeur le plus tôt possible dès qu'elle change — c'est la seule façon de transmettre les données en temps opportun aux intégrations que vous avez configurées. ::: --- # File: kids-mode --- --- title: "Mode Enfants dans le SDK iOS" description: "Activez facilement le mode Enfants pour respecter les politiques Apple. Aucune donnée IDFA ni publicitaire collectée dans le SDK iOS." --- Si votre application iOS est destinée aux enfants, vous devez respecter les politiques d'[Apple](https://developer.apple.com/kids/). Si vous utilisez le SDK Adapty, quelques étapes simples vous permettront de le configurer pour répondre à ces politiques et passer les révisions de l'App Store. ## Qu'est-ce qui est requis ? \{#whats-required\} Vous devez configurer le SDK Adapty pour désactiver la collecte de : - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) - [Adresse IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) De plus, nous recommandons d'utiliser l'identifiant utilisateur client avec précaution. Un identifiant au format `` sera inévitablement considéré comme une collecte de données personnelles, tout comme l'utilisation d'un e-mail. Pour le mode Enfants, la bonne pratique consiste à utiliser des identifiants aléatoires ou anonymisés (par exemple, des identifiants hachés ou des UUID générés par l'appareil) pour garantir la conformité. ## Activation du mode Enfants \{#enabling-kids-mode\} ### Mises à jour dans l'Adapty Dashboard \{#updates-in-the-adapty-dashboard\} Dans l'Adapty Dashboard, vous devez désactiver la collecte d'adresses IP. Pour ce faire, rendez-vous dans [App settings](https://app.adapty.io/settings/general) et cliquez sur **Disable IP address collection** sous **Collect users' IP address**. ### Mises à jour dans le code de votre application mobile \{#updates-in-your-mobile-app-code\} À partir du SDK 4.0, le mode Enfants est un trait de package Swift nommé `KidsMode`. L'activation de ce trait exclut IDFA et AdSupport de l'ensemble du SDK lors de la compilation — vous conservez les modules habituels **Adapty** et **AdaptyUI** ainsi que les instructions d'import habituelles `import Adapty` / `import AdaptyUI`. :::note Le trait `KidsMode` est disponible à partir de la version 4.0 du SDK. À partir du SDK 4.0, le SDK s'installe uniquement via Swift Package Manager — CocoaPods n'est plus pris en charge. ::: 1. [Installez le SDK Adapty](sdk-installation-ios) normalement, en sélectionnant les modules habituels **Adapty** et **AdaptyUI**. 2. Dans Xcode 26.4 ou ultérieur, ouvrez les paramètres de votre projet, accédez à la vue **Package Dependencies** et activez le trait **KidsMode** pour la dépendance AdaptySDK-iOS. :::note Les versions de Xcode antérieures à 26.4 ne permettent pas d'activer des traits pour un projet Xcode depuis l'interface. Dans ce cas, ajoutez un petit package Swift local qui dépend d'Adapty avec le trait `KidsMode` activé (voir l'onglet **Package.swift**), et faites dépendre la cible de votre application de ce package. ::: Si vous ajoutez Adapty comme dépendance dans `Package.swift`, activez le trait dans la déclaration du package. Les traits nécessitent `swift-tools-version` 6.1 ou ultérieur. ```swift showLineNumbers title="Package.swift" .package( url: "https://github.com/adaptyteam/AdaptySDK-iOS.git", from: "4.0.0", traits: ["KidsMode"] ) ``` Si votre application iOS est destinée aux enfants, vous devez respecter les politiques d'[Apple](https://developer.apple.com/kids/). Si vous utilisez le SDK Adapty, quelques étapes simples vous permettront de le configurer pour répondre à ces politiques et passer les révisions de l'App Store. ## Qu'est-ce qui est requis ? \{#whats-required\} Vous devez configurer le SDK Adapty pour désactiver la collecte de : - [IDFA (Identifier for Advertisers)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers) - [Adresse IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) De plus, nous recommandons d'utiliser l'identifiant utilisateur client avec précaution. Un identifiant au format `` sera inévitablement considéré comme une collecte de données personnelles, tout comme l'utilisation d'un e-mail. Pour le mode Enfants, la bonne pratique consiste à utiliser des identifiants aléatoires ou anonymisés (par exemple, des identifiants hachés ou des UUID générés par l'appareil) pour garantir la conformité. ## Activation du mode Enfants \{#enabling-kids-mode\} ### Mises à jour dans l'Adapty Dashboard \{#updates-in-the-adapty-dashboard\} Dans l'Adapty Dashboard, vous devez désactiver la collecte d'adresses IP. Pour ce faire, rendez-vous dans [App settings](https://app.adapty.io/settings/general) et cliquez sur **Disable IP address collection** sous **Collect users' IP address**. ### Mises à jour dans le code de votre application mobile \{#updates-in-your-mobile-app-code\} Pour respecter les politiques, désactivez la collecte de l'IDFA et de l'adresse IP de l'utilisateur. Si vous utilisez Swift Package Manager, vous pouvez activer le mode Enfants en sélectionnant le module **Adapty_KidsMode** dans Xcode lors de l'installation du SDK. Dans Xcode, accédez à **File** -> **Add Package Dependency...**. Notez que les étapes d'ajout de dépendances de package peuvent varier selon les versions de Xcode, référez-vous donc à la documentation Xcode si nécessaire. 1. Saisissez l'URL du dépôt : ``` https://github.com/adaptyteam/AdaptySDK-iOS.git ``` 2. Sélectionnez la version (la dernière version stable est recommandée) et cliquez sur **Add Package**. 3. Dans la fenêtre **Choose Package Products**, sélectionnez les modules dont vous avez besoin : - **Adapty_KidsMode** (module principal) - **AdaptyUI_KidsMode** (optionnel - uniquement si vous prévoyez d'utiliser le Paywall Builder) Vous n'aurez besoin d'aucun autre package. 4. Cliquez sur **Add Package** pour terminer l'installation. 5. Dans votre code, écrivez `import Adapty_KidsMode` à la place de `import Adapty`, et `import AdaptyUI_KidsMode` à la place de `import AdaptyUI` : ```swift ``` 1. Mettez à jour votre Podfile : - Si vous **n'avez pas** de section `post_install`, ajoutez l'intégralité du bloc de code ci-dessous. - Si vous **avez** déjà une section `post_install`, fusionnez les lignes surlignées avec elle. ```ruby showLineNumbers title="Podfile" def adapty_enable_kids_mode(installer) installer.pods_project.targets.each do |target| next unless target.name == 'Adapty' target.build_configurations.each do |config| flags = config.build_settings['OTHER_SWIFT_FLAGS'] || '$(inherited)' flags = flags.join(' ') if flags.is_a?(Array) config.build_settings['OTHER_SWIFT_FLAGS'] = "#{flags} -DADAPTY_KIDS_MODE" end target.frameworks_build_phase.files.dup.each do |bf| target.frameworks_build_phase.remove_build_file(bf) if bf.display_name.to_s.include?('AdSupport') end end installer.pods_project.save Dir.glob(File.join(installer.sandbox.root, 'Target Support Files', '**', '*.xcconfig')).each do |xc| File.write(xc, File.read(xc).gsub(/\s*-framework\s+"?AdSupport"?/, '')) end end post_install do |installer| # ... conservez le contenu existant de votre post_install (Flutter en ajoute un automatiquement) ... adapty_enable_kids_mode(installer) # <-- activer le mode Enfants Adapty end ``` 2. Exécutez la commande suivante pour appliquer les modifications : ```sh showLineNumbers title="Shell" pod install ``` --- # File: ios-onboardings --- --- title: "Onboardings dans le SDK iOS" description: "Découvrez comment utiliser les onboardings dans votre application iOS avec le SDK Adapty." --- :::tip **À partir du SDK v4**, vous pouvez créer des [flows](get-pb-paywalls) comme alternative plus puissante aux onboardings. Contrairement aux onboardings qui s'exécutent dans une WebView, les flows s'affichent nativement sur l'appareil — ce qui offre des animations plus fluides, un rendu cohérent avec l'interface iOS, des temps de chargement plus rapides et aucune dépendance à un runtime WebView. Consultez [Obtenir des flows & paywalls](get-pb-paywalls) et [Afficher des flows & paywalls](ios-present-paywalls) pour commencer. ::: --- # File: get-onboardings --- --- title: "Récupérer les onboardings et leur configuration" description: "Apprenez à récupérer les onboardings dans Adapty." --- :::tip **À partir du SDK v4**, vous pouvez créer des [flows](get-pb-paywalls) comme alternative plus puissante aux onboardings. Contrairement aux onboardings qui s'exécutent dans une WebView, les flows s'affichent nativement sur l'appareil — offrant des animations plus fluides, une expérience iOS cohérente, des temps de chargement plus rapides et aucune dépendance au runtime WebView. Consultez [Obtenir des flows & paywalls](get-pb-paywalls) et [Afficher des flows & paywalls](ios-present-paywalls) pour commencer. ::: Après avoir [conçu la partie visuelle de votre onboarding](design-onboarding) avec le builder dans l'Adapty Dashboard, vous pouvez l'afficher dans votre application mobile. La première étape consiste à récupérer l'onboarding associé au placement ainsi que sa configuration de vue, comme décrit ci-dessous. Avant de commencer, assurez-vous que : 1. Vous avez installé [le SDK Adapty iOS, Android, React Native ou Flutter](installation-of-adapty-sdks) version 3.8.0 ou supérieure. 2. Vous avez [créé un onboarding](create-onboarding). 3. Vous avez ajouté l'onboarding à un [placement](placements). ## Récupérer un onboarding \{#fetch-onboarding\} Lorsque vous créez un [onboarding](onboardings) avec notre builder no-code, il est stocké sous forme de conteneur avec une configuration que votre application doit récupérer et afficher. Ce conteneur gère l'intégralité de l'expérience — le contenu affiché, la façon dont il est présenté, et la manière dont les interactions utilisateur (comme les réponses aux quiz ou les saisies de formulaires) sont traitées. Le conteneur suit également automatiquement les événements analytiques, vous n'avez donc pas besoin d'implémenter un suivi des vues séparé. Pour des performances optimales, récupérez la configuration de l'onboarding suffisamment tôt pour que les images aient le temps de se télécharger avant d'être affichées aux utilisateurs. Pour obtenir un onboarding, utilisez la méthode `getOnboarding` : ```swift showLineNumbers do { let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID") // the requested onboarding } catch { // handle the error } ``` Paramètres : | Paramètre | Présence | Description | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatoire | L'identifiant du [Placement](placements) souhaité. C'est la valeur que vous avez spécifiée lors de la création d'un placement dans l'Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la localisation de l'onboarding. Ce paramètre doit être un code de langue composé d'un ou deux sous-tags séparés par le caractère moins (**-**). Le premier sous-tag correspond à la langue, le second à la région.

Exemple : `en` signifie anglais, `pt-br` représente le portugais brésilien.

Consultez [Localisations et codes de locale](localizations-and-locale-codes) pour plus d'informations sur les codes de locale et nos recommandations d'utilisation.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Cependant, si vos utilisateurs ont une connexion internet instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs ne verront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé après le redémarrage de l'application et n'est effacé qu'en cas de réinstallation ou de nettoyage manuel.

Le SDK Adapty stocke les onboardings localement en deux couches : le cache régulièrement mis à jour décrit ci-dessus et les onboardings de secours. Nous utilisons également un CDN pour récupérer les onboardings plus rapidement et un serveur de secours indépendant en cas d'inaccessibilité du CDN. Ce système est conçu pour vous garantir toujours la dernière version de vos onboardings tout en assurant la fiabilité même lorsque la connexion internet est limitée.

| | **loadTimeout** | par défaut : 5 sec |

Cette valeur limite le délai d'attente de cette méthode. Si le délai est dépassé, les données en cache ou le fallback local seront renvoyés.

Notez que dans de rares cas, cette méthode peut expirer légèrement après le délai spécifié dans `loadTimeout`, car l'opération peut impliquer différentes requêtes en interne.

| Paramètres de réponse : | Paramètre | Description | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | Un objet [`AdaptyOnboarding`](https://swift.adapty.io/documentation/adapty/adaptyonboarding) contenant : l'identifiant et la configuration de l'onboarding, le Remote Config, et plusieurs autres propriétés. | ## Accélérer la récupération des onboardings avec l'onboarding de l'audience par défaut \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} En général, les onboardings sont récupérés presque instantanément, vous n'avez donc pas à vous soucier d'accélérer ce processus. Cependant, si vous avez de nombreuses audiences et onboardings et que vos utilisateurs ont une connexion internet faible, la récupération d'un onboarding peut prendre plus de temps que souhaité. Dans ce cas, vous pouvez afficher un onboarding par défaut pour garantir une expérience fluide plutôt que de ne rien afficher. Pour y remédier, vous pouvez utiliser la méthode `getOnboardingForDefaultAudience`, qui récupère l'onboarding du placement spécifié pour l'audience **All Users**. Il est toutefois essentiel de comprendre que l'approche recommandée est de récupérer l'onboarding via la méthode `getOnboarding`, comme détaillé dans la section [Récupérer un onboarding](#fetch-onboarding) ci-dessus. :::warning Préférez `getOnboarding` à `getOnboardingForDefaultAudience`, car cette dernière présente des limitations importantes : - **Problèmes de compatibilité** : Peut créer des difficultés pour la prise en charge de plusieurs versions d'application, nécessitant soit des designs rétrocompatibles, soit d'accepter que les anciennes versions s'affichent incorrectement. - **Absence de personnalisation** : Affiche uniquement le contenu pour l'audience « All Users », sans ciblage basé sur le pays, l'attribution ou les attributs personnalisés. Si la récupération plus rapide l'emporte sur ces inconvénients dans votre cas, utilisez `getOnboardingForDefaultAudience` comme indiqué ci-dessous. Sinon, utilisez `getOnboarding` comme décrit [ci-dessus](#fetch-onboarding). ::: ```swift showLineNumbers Adapty.getOnboardingForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(onboarding): // the requested onboarding case let .failure(error): // handle the error } } ``` Paramètres : | Paramètre | Présence | Description | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | obligatoire | L'identifiant du [Placement](placements) souhaité. C'est la valeur que vous avez spécifiée lors de la création d'un placement dans l'Adapty Dashboard. | | **locale** |

optionnel

par défaut : `en`

|

L'identifiant de la localisation de l'onboarding. Ce paramètre doit être un code de langue composé d'un ou deux sous-tags séparés par le caractère moins (**-**). Le premier sous-tag correspond à la langue, le second à la région.

Exemple : `en` signifie anglais, `pt-br` représente le portugais brésilien.

Consultez [Localisations et codes de locale](localizations-and-locale-codes) pour plus d'informations sur les codes de locale et nos recommandations d'utilisation.

| | **fetchPolicy** | par défaut : `.reloadRevalidatingCacheData` |

Par défaut, le SDK tente de charger les données depuis le serveur et renvoie les données en cache en cas d'échec. Nous recommandons cette option car elle garantit que vos utilisateurs reçoivent toujours les données les plus récentes.

Cependant, si vos utilisateurs ont une connexion internet instable, envisagez d'utiliser `.returnCacheDataElseLoad` pour renvoyer les données en cache si elles existent. Dans ce cas, les utilisateurs ne verront peut-être pas les toutes dernières données, mais les temps de chargement seront plus rapides quelle que soit la qualité de leur connexion. Le cache est mis à jour régulièrement, il est donc sûr de l'utiliser pendant la session pour éviter les requêtes réseau.

Notez que le cache est conservé après le redémarrage de l'application et n'est effacé qu'en cas de réinstallation ou de nettoyage manuel.

Le SDK Adapty stocke les onboardings localement en deux couches : le cache régulièrement mis à jour décrit ci-dessus et les onboardings de secours. Nous utilisons également un CDN pour récupérer les onboardings plus rapidement et un serveur de secours indépendant en cas d'inaccessibilité du CDN. Ce système est conçu pour vous garantir toujours la dernière version de vos onboardings tout en assurant la fiabilité même lorsque la connexion internet est limitée.

| --- # File: ios-present-onboardings --- --- title: "Présenter les onboardings dans le SDK iOS" description: "Découvrez comment présenter des onboardings sur iOS pour booster vos conversions et vos revenus." --- :::tip **À partir du SDK v4**, vous pouvez créer des [flows](get-pb-paywalls) comme alternative plus puissante aux onboardings. Contrairement aux onboardings qui s'exécutent dans une WebView, les flows s'affichent nativement sur l'appareil — ce qui vous offre des animations plus fluides, un rendu cohérent avec l'interface iOS, des temps de chargement plus rapides et aucune dépendance à l'environnement WebView. Consultez [Obtenir des flows et paywalls](get-pb-paywalls) et [Afficher des flows et paywalls](ios-present-paywalls) pour démarrer. ::: Si vous avez personnalisé un onboarding avec le builder, vous n'avez pas à vous soucier de son rendu dans votre code d'application mobile pour l'afficher à l'utilisateur. Un tel onboarding contient à la fois ce qui doit être affiché et la façon dont il doit l'être. Avant de commencer, assurez-vous que : 1. Vous avez installé [le SDK Adapty iOS](sdk-installation-ios) version 3.8.0 ou ultérieure. 2. Vous avez [créé un onboarding](create-onboarding). 3. Vous avez ajouté l'onboarding à un [placement](placements). ## Présenter des onboardings en Swift \{#present-onboardings-in-swift\} Pour afficher l'onboarding visuel sur l'écran de l'appareil, procédez comme suit : 1. Obtenez la configuration de vue de l'onboarding avec la méthode `.getOnboardingConfiguration`. 2. Initialisez l'onboarding visuel que vous souhaitez afficher avec la méthode `.onboardingController` : Paramètres de la requête : | Paramètre | Présence | Description | |:---------------------------------|:----------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onboarding configuration** | requis | Un objet `AdaptyUI.OnboardingConfiguration` contenant toutes les propriétés de l'onboarding. Utilisez la méthode `AdaptyUI.getOnboardingConfiguration` pour l'obtenir. | | **delegate** | requis | Un `AdaptyOnboardingControllerDelegate` pour écouter les événements de l'onboarding. | Retourne : | Objet | Description | |:---------------------------------|:---------------------------------------------------------------| | **AdaptyOnboardingController** | Un objet représentant l'écran d'onboarding demandé | 3. Une fois l'objet créé avec succès, vous pouvez l'afficher à l'écran de l'appareil : ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI // 0. Get an onboarding if you haven't done it yet let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID") // 1. Obtain the onboarding view configuration: let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding) // 2. Create Onboarding View Controller let onboardingController = try AdaptyUI.onboardingController( with: configuration, delegate: ) // 3. Present it to the user present(onboardingController, animated: true) ``` ## Présenter des onboardings en SwiftUI \{#present-onboardings-in-swiftui\} Pour afficher l'onboarding visuel sur l'écran de l'appareil en SwiftUI : ```swift showLineNumbers title="SwiftUI" // 1. Obtain the onboarding view configuration: let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding) // 2. Display the Onboarding View within your view hierarchy AdaptyOnboardingView( configuration: configuration, placeholder: { Text("Your Placeholder View") }, onCloseAction: { action in // hide the onboarding view }, onError: { error in // handle the error } ) ``` ## Ajouter des transitions fluides entre l'écran de démarrage et l'onboarding \{#add-smooth-transitions-between-the-splash-screen-and-onboarding\} Par défaut, entre l'écran de démarrage et l'onboarding, un écran de chargement s'affiche jusqu'à ce que l'onboarding soit entièrement chargé. Si vous souhaitez rendre cette transition plus fluide, vous pouvez la personnaliser et soit prolonger l'écran de démarrage, soit afficher autre chose. Pour cela, définissez un placeholder (ce qui sera affiché pendant le chargement de l'onboarding). Si vous définissez un placeholder, l'onboarding se chargera en arrière-plan et s'affichera automatiquement une fois prêt. ```swift showLineNumbers extension YourOnboardingManagerClass: AdaptyOnboardingControllerDelegate { func onboardingsControllerLoadingPlaceholder( _ controller: AdaptyOnboardingController ) -> UIView? { // instantiate and return the UIView which will be presented while onboarding is being loaded } } ``` ```swift showLineNumbers AdaptyOnboardingView( configuration: configuration, placeholder: { // define your placeholder view, which will be presented while onboarding is being loaded }, // the rest of the implementation ) ``` ## Personnaliser l'ouverture des liens dans les onboardings \{#customize-how-links-open-in-onboardings\} :::important La personnalisation de l'ouverture des liens dans les onboardings est prise en charge à partir du SDK Adapty v3.15.1. ::: Par défaut, les liens dans les onboardings s'ouvrent dans un navigateur intégré à l'application. Cela offre une expérience utilisateur fluide en affichant les pages web directement dans votre application, sans avoir à changer d'app. Si vous préférez ouvrir les liens dans un navigateur externe, vous pouvez personnaliser ce comportement en définissant le paramètre `externalUrlsPresentation` sur `.externalBrowser` : ```swift showLineNumbers let configuration = try AdaptyUI.getOnboardingConfiguration( forOnboarding: onboarding, externalUrlsPresentation: .externalBrowser // default – .inAppBrowser ) ``` --- # File: ios-handling-onboarding-events --- --- title: "Gérer les événements d'onboarding dans le SDK iOS" description: "Gérez les événements liés à l'onboarding sur iOS avec Adapty." --- :::tip **À partir du SDK v4**, vous pouvez créer des [flows](get-pb-paywalls) comme alternative plus puissante aux onboardings. Contrairement aux onboardings qui s'exécutent dans une WebView, les flows s'affichent nativement sur l'appareil — offrant des animations plus fluides, un rendu cohérent avec iOS, des temps de chargement plus rapides et aucune dépendance au runtime WebView. Consultez [Récupérer les flows et paywalls](get-pb-paywalls) et [Afficher les flows et paywalls](ios-present-paywalls) pour commencer. ::: Avant de commencer, vérifiez que : 1. Vous avez installé le [SDK Adapty iOS](sdk-installation-ios) en version 3.8.0 ou ultérieure. 2. Vous avez [créé un onboarding](create-onboarding). 3. Vous avez ajouté l'onboarding à un [placement](placements). Les onboardings configurés avec le builder génèrent des événements auxquels votre app peut réagir. Découvrez ci-dessous comment gérer ces événements. Pour contrôler ou surveiller les processus qui se déroulent sur l'écran d'onboarding dans votre application mobile, implémentez les méthodes de `AdaptyOnboardingControllerDelegate`. ## Actions personnalisées \{#custom-actions\} Dans le builder, vous pouvez ajouter une action **personnalisée** à un bouton et lui attribuer un identifiant. Vous pouvez ensuite utiliser cet identifiant dans votre code et le traiter comme une action personnalisée. Par exemple, si un utilisateur appuie sur un bouton personnalisé tel que **Login** ou **Allow notifications**, la méthode déléguée `onboardingController` sera déclenchée avec le cas `.custom(id:)` et le paramètre `actionId` correspond à l'**Action ID** défini dans le builder. Vous pouvez créer vos propres identifiants, par exemple "allowNotifications". ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onCustomAction action: AdaptyOnboardingsCustomAction) { if action.actionId == "allowNotifications" { // Request notification permissions } } func onboardingController(_ controller: AdaptyOnboardingController, didFailWithError error: AdaptyUIError) { // Handle errors } ```
Exemple d'événement (cliquer pour développer) ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ```
## Fermeture de l'onboarding \{#closing-onboarding\} L'onboarding est considéré comme fermé lorsqu'un utilisateur appuie sur un bouton auquel l'action **Close** est associée. :::important Notez que vous devez gérer ce qui se passe lorsqu'un utilisateur ferme l'onboarding. Par exemple, vous devez arrêter d'afficher l'onboarding lui-même. ::: Par exemple : ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onCloseAction action: AdaptyOnboardingsCloseAction) { controller.dismiss(animated: true) } ```
Exemple d'événement (cliquer pour développer) ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
## Ouverture d'un paywall \{#opening-a-paywall\} :::tip Gérez cet événement pour ouvrir un paywall si vous souhaitez l'afficher à l'intérieur de l'onboarding. Si vous voulez ouvrir un paywall après la fermeture de l'onboarding, il existe une approche plus directe : gérez [`AdaptyOnboardingsCloseAction`](#closing-onboarding) et ouvrez un paywall sans vous appuyer sur les données de l'événement. ::: La méthode la plus fluide pour utiliser des paywalls dans les onboardings consiste à définir l'action ID comme étant égal à l'identifiant de placement du paywall. Ainsi, après le déclenchement de `AdaptyOnboardingsOpenPaywallAction`, vous pouvez utiliser l'identifiant de placement pour récupérer et ouvrir le paywall immédiatement. Notez qu'une seule vue (paywall ou onboarding) peut être affichée à l'écran à la fois. Si vous présentez un paywall par-dessus un onboarding, vous ne pouvez pas contrôler l'onboarding en arrière-plan par programmation. Tenter de fermer l'onboarding fermera le paywall à la place, laissant l'onboarding visible. Pour éviter cela, fermez toujours la vue de l'onboarding avant de présenter le paywall. ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onPaywallAction action: AdaptyOnboardingsOpenPaywallAction) { // Dismiss onboarding before presenting the flow controller.dismiss(animated: true) { Task { do { // Get the flow using the placement ID from the action let flow = try await Adapty.getFlow(placementId: action.actionId) // Get the flow configuration let flowConfiguration = try await AdaptyUI.getFlowConfiguration( forFlow: flow ) // Create and present the flow controller let flowController = try AdaptyUI.flowController( with: flowConfiguration, delegate: self ) // Present the flow from the root view controller if let rootVC = UIApplication.shared.windows.first?.rootViewController { rootVC.present(flowController, animated: true) } } catch { // Handle any errors that occur during flow loading print("Failed to present flow: \(error)") } } } } ```
Exemple d'événement (cliquer pour développer) ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ```
## Fin du chargement de l'onboarding \{#finishing-loading-onboarding\} Lorsqu'un onboarding finit de se charger, cette méthode est appelée : ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, didFinishLoading action: OnboardingsDidFinishLoadingAction) { // Handle loading completion } ```
Exemple d'événement (cliquer pour développer) ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ```
## Suivi de la navigation \{#tracking-navigation\} La méthode `onAnalyticsEvent` est appelée lorsque divers événements analytiques se produisent durant le flow d'onboarding. L'objet `event` peut être de l'un des types suivants : |Type | Description | |------------|-------------| | `onboardingStarted` | Lorsque l'onboarding a été chargé | | `screenPresented` | Lorsqu'un écran est affiché | | `screenCompleted` | Lorsqu'un écran est complété. Inclut un `elementId` optionnel (identifiant de l'élément complété) et une `reply` optionnelle (réponse de l'utilisateur). Déclenché lorsque les utilisateurs effectuent une action pour quitter l'écran. | | `secondScreenPresented` | Lorsque le deuxième écran est affiché | | `userEmailCollected` | Déclenché lorsque l'e-mail de l'utilisateur est collecté via le champ de saisie | | `onboardingCompleted` | Déclenché lorsqu'un utilisateur atteint un écran avec l'ID `final`. Si vous avez besoin de cet événement, [attribuez l'ID `final` au dernier écran](design-onboarding). | | `unknown` | Pour tout type d'événement non reconnu. Inclut `name` (le nom de l'événement inconnu) et `meta` (métadonnées supplémentaires) | Chaque événement inclut des informations `meta` contenant : | Champ | Description | |------------|-------------| | `onboardingId` | Identifiant unique du flow d'onboarding | | `screenClientId` | Identifiant de l'écran actuel | | `screenIndex` | Position de l'écran actuel dans le flow | | `screensTotal` | Nombre total d'écrans dans le flow | Voici un exemple d'utilisation des événements analytiques pour le suivi : ```swift func onboardingController(_ controller: AdaptyOnboardingController, onAnalyticsEvent event: AdaptyOnboardingsAnalyticsEvent) { switch event { case .onboardingStarted(let meta): // Track onboarding start trackEvent("onboarding_started", meta: meta) case .screenPresented(let meta): // Track screen presentation trackEvent("screen_presented", meta: meta) case .screenCompleted(let meta, let elementId, let reply): // Track screen completion with user response trackEvent("screen_completed", meta: meta, elementId: elementId, reply: reply) case .onboardingCompleted(let meta): // Track successful onboarding completion trackEvent("onboarding_completed", meta: meta) case .unknown(let meta, let name): // Handle unknown events trackEvent(name, meta: meta) // Handle other cases as needed } } ```
Exemples d'événements (cliquer pour développer) ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
--- # File: ios-onboarding-input --- --- title: "Traiter les données des onboardings dans le SDK iOS" description: "Enregistrez et utilisez les données des onboardings dans votre application iOS avec le SDK Adapty." --- :::tip **À partir du SDK v4**, vous pouvez créer des [flows](get-pb-paywalls) comme alternative plus puissante aux onboardings. Contrairement aux onboardings qui s'exécutent dans une WebView, les flows s'affichent nativement sur l'appareil — ce qui vous offre des animations plus fluides, un look and feel iOS cohérent, des temps de chargement plus rapides et aucune dépendance au runtime WebView. Consultez [Obtenir des flows et des paywalls](get-pb-paywalls) et [Afficher des flows et des paywalls](ios-present-paywalls) pour commencer. ::: Lorsque vos utilisateurs répondent à une question de quiz ou saisissent des données dans un champ de texte, la méthode `onStateUpdatedAction` est invoquée. Vous pouvez enregistrer ou traiter le type de champ dans votre code. Par exemple : ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Store user preferences or responses switch action.params { case .select(let params): // Handle single selection case .multiSelect(let params): // Handle multiple selections case .input(let params): // Handle text input case .datePicker(let params): // Handle date selection } } ``` L'objet `action` contient : | Paramètre | Description | |----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `elementId` | Un identifiant unique pour l'élément de saisie. Vous pouvez l'utiliser pour associer les questions aux réponses lors de leur enregistrement. | | `params` | L'objet de données de saisie de l'utilisateur contenant les propriétés de type et de valeur. | | `params.type` | Le type d'élément de saisie. Peut être :
• `"select"` - Sélection unique parmi des options
• `"multiSelect"` - Sélections multiples parmi des options
• `"input"` - Champ de saisie de texte
• `"datePicker"` - Sélection de date | | `params.value` | La ou les valeurs sélectionnées ou saisies par l'utilisateur. La structure dépend du type :
• `select` : Objet avec `id`, `value`, `label`
• `multiSelect` : Tableau d'objets avec `id`, `value`, `label`
• `input` : Objet avec `type`, `value`
• `datePicker` : Objet avec `day`, `month`, `year` |
Exemples de données enregistrées (peuvent différer selon votre implémentation) ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ```
## Cas d'usage \{#use-cases\} ### Enrichir les profils utilisateurs avec des données \{#enrich-user-profiles-with-data\} Si vous souhaitez associer immédiatement les données saisies au profil utilisateur et éviter de lui demander deux fois les mêmes informations, vous devez [mettre à jour le profil utilisateur](setting-user-attributes) avec les données saisies lors du traitement de l'action. Par exemple, vous demandez aux utilisateurs de saisir leur nom dans le champ texte avec l'ID `name`, et vous souhaitez définir la valeur de ce champ comme prénom de l'utilisateur. Vous leur demandez également de saisir leur e-mail dans le champ `email`. Dans le code de votre application, cela peut ressembler à ceci : ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Store user preferences or responses switch action.params { case .input(let params): // Handle text input let builder = AdaptyProfileParameters.Builder() // Map elementId to appropriate profile field switch action.elementId { case "name": builder.with(firstName: params.value.value) case "email": builder.with(email: params.value.value) default: break } // Delegate methods are synchronous; kick off the async update in a Task. Task { do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } } default: break } } ``` ### Personnaliser les paywalls selon les réponses \{#customize-paywalls-based-on-answers\} En utilisant des quiz dans les onboardings, vous pouvez également personnaliser les paywalls affichés aux utilisateurs après qu'ils ont terminé l'onboarding. Par exemple, vous pouvez interroger les utilisateurs sur leur expérience sportive et afficher des CTA et des produits différents selon les groupes d'utilisateurs. 1. [Ajoutez un quiz](onboarding-quizzes) dans le constructeur d'onboarding et assignez des ID significatifs à ses options. 2. Traitez les réponses au quiz en fonction de leurs ID et [définissez des attributs personnalisés](setting-user-attributes) pour les utilisateurs. ```swift showLineNumbers func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) { // Handle quiz responses and set custom attributes switch action.params { case .select(let params): // Handle quiz selection let builder = AdaptyProfileParameters.Builder() // Map quiz responses to custom attributes switch action.elementId { case "experience": // Set custom attribute 'experience' with the selected value (beginner, amateur, pro) try? builder.with(customAttribute: params.value.value, forKey: "experience") default: break } // Delegate methods are synchronous; kick off the async update in a Task. Task { do { try await Adapty.updateProfile(params: builder.build()) } catch { // handle the error } } default: break } } ``` 3. [Créez des segments](segments) pour chaque valeur d'attribut personnalisé. 4. Créez un [placement](placements) et ajoutez des [audiences](audience) pour chaque segment créé. 5. [Affichez un paywall](ios-paywalls) pour le placement dans le code de votre application. Si votre onboarding comporte un bouton qui ouvre un paywall, implémentez le code du paywall en tant que [réponse à l'action de ce bouton](ios-handling-onboarding-events#opening-a-paywall). --- # File: ios-best-practices --- --- title: "Meilleures pratiques avec le SDK iOS" description: "Modèles de référence pour intégrer le SDK Adapty sur iOS — ordre des appels, gestion des erreurs et autres règles de production." --- --- # File: ios-sdk-call-order --- --- title: "Ordre d'appel dans le SDK iOS" description: "Évitez la perte d'accès premium, les attributions manquantes et les erreurs intermittentes #2002 en appelant les méthodes du SDK Adapty dans le bon ordre." --- `Adapty.activate()` doit se terminer avant tout autre appel de méthode du SDK Adapty. Tant qu'il n'est pas résolu, le SDK n'a aucun état. Tout appel émis avant ou en parallèle de `activate()` échoue avec [`#2002 notActivated`](ios-sdk-error-handling#network-errors). Si votre application authentifie les utilisateurs et que vous récupérez un identifiant utilisateur client après le lancement, appelez `Adapty.identify()` à ce moment-là. N'appelez pas de méthodes liées aux actions utilisateur avant que `identify` ne soit résolu. Les appels qui s'exécutent en parallèle échouent avec [`#3006 profileWasChanged`](ios-sdk-error-handling#general-errors), ou atterrissent sur le profil anonyme créé à l'activation. Lorsque cela se produit, l'attribution, les identifiants MMP comme `appsflyer_id`, et la propriété d'installation ne sont pas toujours transférés vers le profil identifié. Si votre application n'authentifie pas les utilisateurs, ignorez `identify` et continuez à travailler avec le profil anonyme. Les SDK MMP et d'analytics (AppsFlyer, Adjust, Branch, PostHog) suivent la même règle. Initialisez-les en premier et attendez leurs callbacks d'UID avant d'appeler `Adapty.activate`. Sinon, l'identifiant MMP atterrit sur un profil anonyme éphémère et n'est pas toujours transféré vers le profil identifié. Pour les spécificités d'AppsFlyer, consultez [AppsFlyer](appsflyer). ## L'ordre correct \{#the-correct-order\} Votre parcours dépend de deux choses : le moment où vous connaissez l'identifiant utilisateur client, et si vous utilisez un SDK MMP ou d'analytics. - **Étapes 2 et 5** : Obligatoires pour chaque application. Activez le SDK, puis appelez les méthodes du SDK. - **Étapes 1 et 3** : Requises uniquement si vous intégrez un SDK MMP ou d'analytics (AppsFlyer, Adjust, Branch, PostHog). - **Étape 4** : Requise uniquement si votre application authentifie les utilisateurs et récupère l'identifiant utilisateur client après le lancement. Si vous disposez de l'identifiant utilisateur client au lancement de l'application, passez-le directement dans `activate()` (étape 2a). Ce chemin ne crée jamais de profil anonyme, donc l'étape 4 est inutile. | Étape | Appel | Quand | Notes | |-------|---------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | Initialisez votre SDK MMP ou d'analytics (AppsFlyer, Adjust, PostHog, Branch) | Lancement de l'application, en premier | Attendez le callback d'UID du MMP, par exemple `getAppsFlyerUID`. | | 2a | `Adapty.activate(with: config)` avec `customerUserId` défini sur la config | Lancement de l'application, après l'étape 1, si vous avez l'identifiant utilisateur client | Recommandé. Aucun profil anonyme n'est jamais créé. | | 2b | `Adapty.activate(with: config)` sans `customerUserId` | Lancement de l'application, après l'étape 1, si vous n'avez pas l'identifiant utilisateur client (ou ne le collectez jamais) | Adapty crée un profil anonyme. | | 3 | `Adapty.setIntegrationIdentifier(...)` pour chaque MMP | Après l'étape 2, avant tout appel lié aux actions utilisateur | Requis pour que les identifiants MMP atterrissent sur le bon profil. | | 4 | `try await Adapty.identify("YOUR_USER_ID")` | Après l'étape 3 (ou l'étape 2 si pas de MMP), avant l'étape 5 — uniquement sur le chemin 2b avec authentification | Toujours `await`. Les appels concurrents pendant `identify` produisent `#3006 profileWasChanged`. | | 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Après l'étape 4 si vous appelez `identify` ; sinon après l'étape 3 (ou l'étape 2 si pas de MMP) | Ces appels nécessitent un profil stable. | :::important Ignorer ces étapes entraîne la perte d'accès premium pour les utilisateurs de retour, l'absence d'`appsflyer_id` sur les profils, et des paywalls renvoyés pour la mauvaise audience. ::: ## Installations web2app et web-funnel \{#web2app-and-web-funnel-installs\} Si les utilisateurs achètent via un checkout web (Stripe, Paddle, FunnelFox) et installent ensuite l'application native, le premier `activate()` sur l'appareil crée un nouveau profil anonyme. Ce profil n'est pas lié au profil web. Si vous pouvez résoudre l'identifiant utilisateur client avant le lancement de l'application (depuis votre flux d'authentification ou le référent d'installation), passez-le directement dans `activate()`. Sinon, l'achat web reste invisible sur l'appareil jusqu'à ce que vous appeliez `identify("YOUR_USER_ID")` puis `restorePurchases`. Pour les métadonnées à envoyer avec chaque checkout web, consultez : - [Stripe](stripe) - [Paddle](paddle) --- # File: ios-optimize-paywall-fetching --- --- title: "Optimiser la récupération des paywalls dans le SDK iOS" description: "Récupérez les paywalls Adapty de manière fiable : timing, mise en cache et patterns de secours pour iOS." --- Une récupération de paywall fiable sur iOS repose sur trois éléments : un rendu rapide, le retour du paywall ciblé par audience, et un repli élégant en cas de réseau lent. Les règles ci-dessous couvrent le timing, la mise en cache et les patterns de secours pour y parvenir. :::tip Ces règles supposent que `Adapty.activate()` et `Adapty.identify()` ont déjà été résolus. Voir [Ordre d'appel dans le SDK iOS](ios-sdk-call-order). ::: ## Règles et pièges \{#rules-and-pitfalls\} | À faire | À éviter | Pourquoi | |---|---|---| | Récupérez le placement que vous êtes sur le point d'afficher. | Pré-charger tous les placements simultanément au lancement. | La pré-récupération en masse bloque le thread principal et provoque un écran noir pendant la salve. | | Appelez `getPaywall` après que l'attribution a eu le temps de se résoudre — par exemple, 1 à 2 secondes après `activate` ou après le déclenchement de `onProfileUpdate`. | Appeler `getPaywall` dans `App.init()`. | L'attribution n'est pas encore disponible. Le paywall se résout sur l'audience par défaut et ignore silencieusement les segments et la personnalisation ASA. | | Définissez un `loadTimeout` et configurez un [paywall de secours](fallback-paywalls) pour chaque placement. | Attendre indéfiniment sur `getPaywall`. | Sans timeout, les utilisateurs avec une mauvaise connexion voient un écran vide jusqu'à ce que le réseau réponde — ou ferment l'application. | Consultez [Récupérer les paywalls et les produits](fetch-paywalls-and-products) pour la référence des paramètres `fetchPolicy` et `loadTimeout`, et [Placements](placements) pour choisir le bon placement. ## Optimiser pour une mauvaise connectivité \{#tune-for-poor-connectivity\} Pour les marchés avec une connectivité systématiquement mauvaise (zones rurales, transports en commun, régions affectées par le routage) : - Définissez `fetchPolicy: .returnCacheDataElseLoad` sur chaque récupération sauf la toute première. - Configurez un [paywall de secours](fallback-paywalls) pour chaque placement dans le tableau de bord Adapty. - Définissez `loadTimeout` entre 3 et 5 secondes et acceptez le paywall de secours lorsque le timeout se déclenche. - Ne conditionnez pas l'affichage du paywall à `getProfile()`. Appelez `getPaywall` indépendamment pour qu'un profil lent ne bloque pas l'interface. --- # File: ios-show-aa-targeted-paywall --- --- title: "Afficher un paywall ciblé par AA au premier lancement dans le SDK iOS" description: "Attendez l'attribution Apple Ads avant de demander le paywall sur iOS en utilisant AdaptyProfile.appliedAttributionSources." --- L'attribution Apple Ads (AA) arrive de manière asynchrone après `Adapty.activate()`. Si vous appelez `getPaywall` trop tôt, l'attribution n'a souvent pas encore été reçue et Adapty résout le placement par rapport à l'audience par défaut — contournant ainsi vos paywalls segmentés par AA. `AdaptyProfile.appliedAttributionSources` permet à l'app de détecter quand l'attribution AA a été appliquée au profil, afin que la requête de paywall puisse attendre que la segmentation AA se résolve correctement. ## Avant de commencer \{#before-you-start\} Vous avez besoin de : - SDK Adapty iOS **3.17.1** ou version ultérieure. - Apple Ads configuré pour l'app dans Adapty. Voir [Apple Ads](apple-search-ads). ## Fonctionnement \{#how-it-works\} Après `Adapty.activate()`, le SDK demande l'attribution Apple Ads à Apple en arrière-plan et transmet le résultat au backend d'Adapty. Lorsqu'AA devient la source d'attribution active pour le profil, le SDK délivre un `AdaptyProfile` mis à jour dont le tableau `appliedAttributionSources` contient `.appleAds`. Un tableau vide peut signifier l'un des cas suivants : - L'attribution Apple Ads n'a pas encore été traitée pour ce profil. - Aucune attribution n'est arrivée du tout. Même avec un tableau vide, `getPaywall` reste sûr à appeler — Adapty résout la requête par rapport à l'audience qui correspond à l'état actuel du profil, généralement l'audience par défaut. :::important L'attente s'applique uniquement au **premier lancement**. Une fois l'attribution Apple Ads enregistrée, elle est stockée définitivement sur le profil. À chaque lancement suivant, le profil en cache contient déjà `.appleAds` dans `appliedAttributionSources`, `didLoadLatestProfile` se déclenche immédiatement avec cette valeur, et `getPaywall` retourne le paywall segmenté par Apple Ads sans aucun délai. ::: ## Implémentation \{#implementation\} Au premier lancement, surveillez `.appleAds` dans le profil et appliquez un délai d'expiration strict — si l'attribution Apple Ads n'arrive jamais, ces utilisateurs doivent quand même voir un paywall. 1. **Activez le SDK.** Voir [Installer et configurer le SDK iOS](sdk-installation-ios). 2. **Abonnez-vous aux mises à jour du profil** en vous conformant à `AdaptyDelegate` et en implémentant `didLoadLatestProfile`. Si vous n'avez pas encore configuré le délégué, voir [Écouter les mises à jour d'abonnement](ios-check-subscription-status#listen-to-subscription-updates). 3. **Surveillez `.appleAds` dans `appliedAttributionSources`.** Lorsqu'il apparaît, demandez le paywall — Adapty retournera la variante segmentée par AA : ```swift extension : AdaptyDelegate { nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { if profile.appliedAttributionSources.contains(where: { $0 == .appleAds }) { // load paywall via Adapty.getPaywall(placementId:) } } } ``` 4. **Démarrez un minuteur de 3 à 5 secondes en parallèle de l'abonnement.** Si le minuteur se déclenche avant l'apparition de `.appleAds`, demandez le paywall quand même : La première des deux voies à se déclencher doit charger le paywall ; l'autre doit être ignorée. Utilisez un indicateur d'état unique (par exemple, `hasLoadedPaywall`) pour éviter les doublons afin que le paywall ne soit pas récupéré deux fois. Configurez un [paywall de secours](fallback-paywalls) pour le placement afin que l'utilisateur ne reste jamais bloqué si la requête réseau échoue. ## Exemple complet \{#complete-example\} L'implémentation ci-dessous met en compétition l'attribution contre un délai d'expiration, pré-charge le paywall de l'audience par défaut en parallèle, et retourne le paywall approprié. L'appelant attend une seule fonction async — pas de délégués ni d'indicateurs d'état à gérer côté appelant. `ProfileObserver` est un singleton réutilisable qui publie les mises à jour de profil depuis `AdaptyDelegate`. `PaywallLoader.getPaywallOrDefault` lance la course en utilisant un `TaskGroup` à concurrence structurée : - Si l'attribution arrive avant `timeout`, il retourne le paywall segmenté via `getPaywall(placementId:)`. - Si `timeout` s'écoule en premier, il retourne le paywall de l'audience par défaut pré-chargé via `getPaywallForDefaultAudience(placementId:)`. ```swift title="PaywallLoader.swift" /// Demonstrates how to fetch a paywall that depends on attribution being applied, /// falling back to the default-audience paywall if attribution doesn't arrive in time. /// /// Stateless and self-contained: every call kicks off its own default-audience /// prefetch and races it against attribution + segmented fetch. enum PaywallLoader { static func getPaywallOrDefault( placementId: String, timeout: TimeInterval ) async throws -> AdaptyPaywall { struct TimedOut: Error {} // Kick off the default-audience request immediately so it has the full // `timeout` window to load. We'll either cancel it on success or await // its result on timeout — never a duplicate network call. let defaultPaywallTask = Task { try await Adapty.getPaywallForDefaultAudience(placementId: placementId) } do { // Race two child tasks: whichever finishes first wins. let result = try await withThrowingTaskGroup(of: AdaptyPaywall.self) { group in // 1. Wait for attribution, then ask Adapty for the segmented paywall. group.addTask { await waitForAttribution() return try await Adapty.getPaywall(placementId: placementId) } // 2. Time-bomb: throws `TimedOut` after `timeout` seconds. group.addTask { try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000)) throw TimedOut() } guard let value = try await group.next() else { throw CancellationError() } group.cancelAll() // stop the loser (sleeper or the attribution wait). return value } // Segmented paywall won — we no longer need the default-audience prefetch. defaultPaywallTask.cancel() return result } catch is TimedOut { // Attribution didn't apply in time — return the prefetched default // (instant if already done, otherwise we await the in-flight request). return try await defaultPaywallTask.value } } /// Suspends until a profile with the desired attribution source is observed. /// `@Published.values` emits the current profile immediately on subscription, /// so this returns on the first iteration if attribution is already applied. @MainActor private static func waitForAttribution() async { for await profile in ProfileObserver.shared.$profile.values { if profile?.appliedAttributionSources.contains(.appleAds) == true { return } } } } @MainActor final class ProfileObserver: AdaptyDelegate { static let shared = ProfileObserver() @Published private(set) var profile: AdaptyProfile? nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) { Task { @MainActor [weak self] in self?.profile = profile } } } ``` Reliez `ProfileObserver` à `AdaptyDelegate` une seule fois, après la complétion de `Adapty.activate()` : ```swift Adapty.delegate = ProfileObserver.shared ``` Appelez depuis l'écran de démarrage : ```swift do { let paywall = try await PaywallLoader.getPaywallOrDefault( placementId: "YOUR_PLACEMENT_ID", timeout: 5 ) // present the paywall } catch { // handle the error or show a fallback paywall } ``` Si votre app utilise déjà un `AdaptyDelegate` à d'autres fins (par exemple, [écouter les mises à jour d'abonnement](ios-check-subscription-status#listen-to-subscription-updates)), transmettez `didLoadLatestProfile` à `ProfileObserver.shared` depuis votre délégué existant plutôt que de définir `Adapty.delegate = ProfileObserver.shared`. --- # File: ios-test --- --- title: "Test & release in iOS SDK" description: "Découvrez comment vérifier le statut d'abonnement dans votre application iOS avec Adapty." --- Si vous avez déjà intégré le SDK Adapty dans votre application iOS, vous souhaitez vérifier que tout est correctement configuré et que les achats fonctionnent comme prévu. Cela implique de tester à la fois l'intégration du SDK et les achats réels. ## Tester votre application \{#test-your-app\} Pour des tests complets de vos achats intégrés, notamment les tests en sandbox et la validation via TestFlight, consultez notre [guide de test](test-purchases-in-sandbox). ## Préparer la mise en production \{#prepare-for-release\} Avant de soumettre votre application au store, suivez la [checklist de mise en production](release-checklist) pour confirmer que : - La connexion au store et les notifications serveur sont configurées - Les achats sont effectués et signalés à Adapty - L'accès est déverrouillé et restauré correctement - Les exigences en matière de confidentialité et de révision sont respectées --- # File: ios-reference --- --- title: "Référence pour le SDK iOS" description: "Documentation de référence pour le SDK iOS Adapty." --- Cette page contient la documentation de référence pour le SDK iOS Adapty. Choisissez le sujet dont vous avez besoin : - **[Modèles SDK](https://swift.adapty.io/)** - Modèles de données et structures utilisés par le SDK - **[Gérer les erreurs](ios-sdk-error-handling)** - Gestion des erreurs et résolution des problèmes --- # File: ios-sdk-error-handling --- --- title: "Gérer les erreurs dans le SDK iOS" description: "Gérez efficacement les erreurs du SDK iOS avec le guide de dépannage d'Adapty." --- Le SDK Adapty dispose de son propre wrapper pour tout type d'erreur, appelé `AdaptyError`. En pratique, chaque erreur renvoyée par le SDK est un `AdaptyError`. Il possède deux propriétés utiles : `originalError` et `adaptyErrorCode`, décrites ci-dessous. **originalError** contient l'erreur d'origine si vous en avez besoin. Il peut s'agir d'une [SKError](https://developer.apple.com/documentation/storekit/skerror), d'une [NSError](https://developer.apple.com/documentation/foundation/nserror) ou d'une [Error](https://developer.apple.com/documentation/swift/error) Swift générique. Cette propriété est optionnelle, car certaines erreurs peuvent être générées directement par le SDK — par exemple, des données incohérentes ou manquantes — et ne disposent pas d'erreur d'origine autour de laquelle le wrapper a été initialement construit. **adaptyErrorCode** peut être utilisé pour gérer les problèmes courants, comme : - des identifiants invalides - des erreurs réseau - des paiements annulés - des problèmes de facturation - un reçu invalide - et bien plus encore Il est très simple de vérifier le code d'erreur et d'y réagir en conséquence. ```swift showLineNumbers title="Swift" do { let info = try await Adapty.makePurchase(product: product) } catch { if error.adaptyErrorCode == .paymentCancelled { // purchase was cancelled // you can offer discount to your user or remind them later } } ``` :::tip **Activez les logs détaillés avant de déboguer.** La plupart des `AdaptyError` encapsulent une erreur StoreKit, réseau ou backend sous-jacente. Avec les logs détaillés activés (`Adapty.logLevel = .verbose` — voir [Logging](sdk-installation-ios#logging)), cette erreur encapsulée est affichée dans la console, ce qui révèle généralement la cause réelle. La propriété `originalError` est renseignée quel que soit le niveau de log — les logs détaillés permettent simplement de la faire apparaître dans la console. ::: :::important Si ces solutions ne résolvent pas votre problème, consultez la section [Autres problèmes](#other-issues) pour connaître les étapes à suivre avant de contacter le support, afin de nous aider à vous assister plus efficacement. ::: ## Erreurs StoreKit \{#storekit-errors\} | Erreur | Code | Solution | |--------------------------------------------------------------------------------------------------------------------------------------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [unknown](https://developer.apple.com/documentation/storekit/skerror/code/unknown) | 0 | Code d'erreur indiquant qu'une erreur inconnue ou inattendue s'est produite.
Réessayez ou consultez la section [Autres problèmes](#other-issues). | | [clientInvalid](https://developer.apple.com/documentation/storekit/skerror/code/clientinvalid) | 1 | Ce code d'erreur indique que le client n'est pas autorisé à effectuer l'action tentée. | | [paymentCancelled](https://developer.apple.com/documentation/storekit/skerror/code/paymentcancelled) | 2 |

Ce code d'erreur indique que l'utilisateur a annulé une demande de paiement.

Aucune action n'est requise, mais d'un point de vue métier, vous pouvez proposer une réduction à votre utilisateur ou lui rappeler plus tard.

| | [paymentInvalid](https://developer.apple.com/documentation/storekit/skerror/code/paymentinvalid) | 3 | Cette erreur indique qu'un des paramètres de paiement n'a pas été reconnu par l'App Store. | | [paymentNotAllowed](https://developer.apple.com/documentation/storekit/skerror/code/paymentnotallowed) | 4 | Ce code d'erreur indique que l'utilisateur n'est pas autorisé à valider des paiements. | | [storeProductNotAvailable](https://developer.apple.com/documentation/storekit/skerror/code/storeproductnotavailable) | 5 | Ce code d'erreur indique que le produit demandé n'est pas disponible dans le store.
Essayez de réinstaller l'application. | | [cloudServicePermissionDenied](https://developer.apple.com/documentation/storekit/skerror/code/cloudservicepermissiondenied) | 6 | Ce code d'erreur indique que l'utilisateur n'a pas autorisé l'accès aux informations du service Cloud. | | [cloudServiceNetworkConnectionFailed](https://developer.apple.com/documentation/storekit/skerror/code/cloudservicenetworkconnectionfailed) | 7 | Ce code d'erreur indique que l'appareil n'a pas pu se connecter au réseau. | | [cloudServiceRevoked](https://developer.apple.com/documentation/storekit/skerror/code/cloudservicerevoked/) | 8 | Ce code d'erreur indique que l'utilisateur a révoqué l'autorisation d'utiliser ce service Cloud. | | [privacyAcknowledgementRequired](https://developer.apple.com/documentation/storekit/skerror/code/privacyacknowledgementrequired) | 9 | Ce code d'erreur indique que l'utilisateur n'a pas encore accepté la politique de confidentialité d'Apple. | | [unauthorizedRequestData](https://developer.apple.com/documentation/storekit/skerror/code/unauthorizedrequestdata) | 10 | Ce code d'erreur indique que l'application tente d'utiliser une propriété pour laquelle elle ne dispose pas des droits requis. | | [invalidOfferIdentifier](https://developer.apple.com/documentation/storekit/skerror/code/invalidofferidentifier) | 11 |

L'[`identifiant`](https://developer.apple.com/documentation/storekit/skpaymentdiscount/identifier) de l'offre n'est pas valide. Par exemple, vous n'avez pas configuré d'offre avec cet identifiant dans l'App Store, ou vous avez révoqué l'offre.

Assurez-vous de configurer les offres souhaitées dans App Store Connect et de transmettre un identifiant d'offre valide.

| | [invalidSignature](https://developer.apple.com/documentation/storekit/skerror/code/invalidsignature) | 12 | Ce code d'erreur indique que la signature dans une remise de paiement n'est pas valide. | | [missingOfferParams](https://developer.apple.com/documentation/storekit/skerror/code/missingofferparams) | 13 | Ce code d'erreur indique que des paramètres sont manquants dans une remise de paiement. | | [invalidOfferPrice](https://developer.apple.com/documentation/storekit/skerror/code/invalidofferprice/) | 14 | Ce code d'erreur indique que le prix que vous avez spécifié dans App Store Connect n'est plus valide. Les offres doivent toujours correspondre à un prix réduit. | | noProductIDsFound | 1000 |

Cette erreur indique qu'aucun des produits que vous avez demandés sur le paywall n'est disponible à l'achat dans l'App Store, même s'ils y sont répertoriés. Cette erreur peut parfois s'accompagner d'un avertissement `InvalidProductIdentifiers`. Si l'avertissement apparaît sans erreur, ignorez-le.

Si vous rencontrez cette erreur, suivez les étapes de la section [Correction de l'erreur Code-1000 `noProductIDsFound`](InvalidProductIdentifiers).

| | productRequestFailed | 1002 | Impossible de récupérer les produits disponibles pour le moment. | | cantMakePayments | 1003 | Les achats intégrés ne sont pas autorisés sur cet appareil. Consultez le [guide](cantMakePayments) de dépannage. | | [cantReadReceipt](https://developer.apple.com/documentation/storekit/skerror/code/paymentcancelled) | 1005 |

Aucun reçu valide n'est disponible sur l'appareil. Cela peut poser problème lors des tests en sandbox.

En sandbox, vous n'aurez pas de fichier de reçu valide tant que vous n'aurez pas effectué un achat, assurez-vous donc d'en faire un avant d'y accéder. Lors des tests en sandbox, vérifiez également que vous êtes connecté sur l'appareil avec un compte sandbox Apple valide.

| | productPurchaseFailed | 1006 | L'achat du produit a échoué. Cette erreur encapsule une erreur StoreKit sous-jacente — lisez `originalError` (ou activez les logs détaillés pour la voir dans la console) pour connaître la raison réelle. L'erreur encapsulée correspond généralement à l'un des codes StoreKit 0–14 du tableau ci-dessus — le plus souvent `paymentCancelled`, `paymentInvalid`, `paymentNotAllowed` ou `invalidOfferPrice`. Si vous ne pouvez pas identifier une raison précise, essayez un nouveau [profil sandbox](test-purchases-in-sandbox) ; si le problème persiste, contactez le support Apple. | | refreshReceiptFailed | 1010 | L'opération de rafraîchissement du reçu a échoué. | | fetchSubscriptionStatusFailed | 1020 | Impossible de récupérer le statut de l'abonnement depuis l'App Store. | | unknownTransactionId | 1030 | L'identifiant de transaction est inconnu. | | paymentPendingError | 1050 | Le paiement est actuellement en attente. | ## Erreurs réseau \{#network-errors\} | Erreur | Code | Solution | | :------------- | :--- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | notActivated | 2002 | Le SDK Adapty n'est pas activé.
Cela se produit généralement lorsqu'un écran de démarrage ou un hook d'interface utilisateur précoce appelle des méthodes Adapty avant que `Adapty.activate` ne retourne. Le symptôme est intermittent et peut ne pas se reproduire sur simulateur, car le timing est différent sur un vrai appareil. Attendez le handler de complétion ou le résultat async d'`activate` avant de planifier tout autre appel SDK. Voir [Ordre des appels dans le SDK iOS](ios-sdk-call-order) pour la séquence complète. | | badRequest | 2003 | Requête incorrecte.
Vérifiez que vous avez bien effectué toutes les étapes nécessaires à l'[intégration avec l'App Store](app-store-connection-configuration). | | serverError | 2004 | Erreur serveur.
Réessayez après un moment. Si le problème persiste, contactez l'équipe support Adapty. | | networkFailed | 2005 | Cette erreur indique des problèmes de connexion réseau sur l'appareil de l'utilisateur.
Essayez de désactiver le VPN ou de basculer entre le Wi-Fi et le réseau cellulaire. | | decodingFailed | 2006 | Cette erreur indique que le décodage de la réponse a échoué.
Vérifiez votre code et assurez-vous que les paramètres que vous envoyez sont valides. Par exemple, cette erreur peut indiquer que vous utilisez une clé API invalide. | | encodingFailed | 2009 | Cette erreur indique que l'encodage de la requête a échoué. | ## Erreurs générales \{#general-errors\} | Erreur | Code | Solution | | :------------------- | :--- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | analyticsDisabled | 3000 | Impossible de traiter les événements analytics, car vous avez [désactivé cette option](analytics-integration#disabling-external-analytics-for-a-specific-customer). | | wrongParam | 3001 | Cette erreur indique que certains de vos paramètres sont incorrects.
Si vous utilisez le Paywall Builder d'Adapty et ne pouvez pas afficher un paywall à cause de cette erreur, activez l'option **Show on device** dans le Paywall Builder.
Une autre cause possible est que la version du fichier [fallback](fallback-paywalls) local ne correspond pas à la version du SDK. Téléchargez un nouveau fichier depuis le tableau de bord. | | activateOnceError | 3005 | Il n'est pas possible d'appeler la méthode `.activate` plus d'une fois. | | profileWasChanged | 3006 | Le profil utilisateur a été modifié pendant l'opération.
Cela se produit lorsqu'une méthode est appelée alors qu'`Adapty.identify` est encore en cours d'exécution — l'appel en vol atterrit sur un profil sur le point d'être remplacé, et le SDK le rejette. Utilisez toujours `await` sur `identify` (ou son handler de complétion) avant tout appel déclenché par une action utilisateur. Voir [Ordre des appels dans le SDK iOS](ios-sdk-call-order). | | unsupportedData | 3007 | Cette erreur indique que le format de données n'est pas pris en charge par le SDK. | | unidentifiedUserLogout | 3020 | Il n'est pas possible d'appeler la méthode `logout` pour un utilisateur non identifié. | | fetchTimeoutError | 3101 | Cette erreur indique que l'opération de récupération a expiré. | | operationInterrupted | 9000 | Cette opération a été interrompue par le système. | ## Autres problèmes \{#other-issues\} Si vous n'avez pas encore trouvé de solution, voici les prochaines étapes possibles : - **Mettre à jour le SDK vers la dernière version** : nous recommandons toujours de passer à la dernière version du SDK, car elles sont plus stables et incluent des correctifs pour les problèmes connus. - **Contacter l'équipe support ou obtenir de l'aide auprès d'autres développeurs** sur le [forum de support](https://adapty.featurebase.app/). - **Contacter l'équipe support via [support@adapty.io](mailto:support@adapty.io) ou via le chat** : si vous n'êtes pas prêt à mettre à jour le SDK ou si cela n'a pas résolu le problème, contactez notre équipe support. Notez que votre problème sera résolu plus rapidement si vous [activez les logs détaillés](sdk-installation-ios#logging) et les partagez avec l'équipe. Vous pouvez également joindre des extraits de code pertinents. --- # File: InvalidProductIdentifiers --- --- title: "Correction de l'erreur Code-1000 noProductIDsFound" description: "Résolvez les erreurs d'identifiants de produits invalides lors de la gestion des abonnements dans Adapty." --- L'erreur code 1000, `noProductIDsFound`, indique qu'aucun des produits demandés sur le paywall n'est disponible à l'achat dans l'App Store, même s'ils y sont bien référencés. Cette erreur peut parfois s'accompagner d'un avertissement `InvalidProductIdentifiers`. Si l'avertissement apparaît sans erreur, ignorez-le sans crainte. Si vous rencontrez l'erreur `noProductIDsFound`, suivez ces étapes pour la résoudre : ## Étape 1. Vérifier le bundle ID \{#step-2-check-bundle-id\} 1. Ouvrez [App Store Connect](https://appstoreconnect.apple.com/apps). Sélectionnez votre application et accédez à la section **General** → **App Information**. 2. Copiez le **Bundle ID** dans la sous-section **General Information**. 3. Ouvrez l'onglet [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) depuis le menu supérieur d'Adapty et collez la valeur copiée dans le champ **Bundle ID**. 4. Revenez à la page **App information** dans App Store Connect et copiez l'**Apple ID** qui s'y trouve. 5. Sur la page [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) dans l'Adapty Dashboard, collez l'identifiant dans le champ **Apple app ID**. ## Étape 2. Vérifier les produits 1. Rendez-vous dans **App Store Connect** et accédez à [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) dans le menu de gauche. 2. Cliquez sur le nom du groupe d'abonnements. Vos produits s'affichent dans la section **Subscriptions**. 3. Assurez-vous que le produit testé est bien marqué **Ready to Submit**. Si ce n'est pas le cas, suivez les instructions sur la page [Produit dans l'App Store](app-store-products). 4. Comparez l'identifiant du produit dans le tableau avec celui de l'onglet [**Products**](https://app.adapty.io/products) dans l'Adapty Dashboard. Si les identifiants ne correspondent pas, copiez l'identifiant depuis le tableau et [créez un produit](create-product) avec cet identifiant dans l'Adapty Dashboard. ## Étape 3. Vérifier la disponibilité du produit \{#step-4-check-product-availability\} 1. Retournez dans **App Store Connect** et ouvrez la même section **Subscriptions**. 2. Cliquez sur le nom du groupe d'abonnements pour afficher vos produits. 3. Sélectionnez le produit que vous testez. 4. Faites défiler jusqu'à la section **Availability** et vérifiez que tous les pays et régions requis y figurent. ## Étape 4. Vérifier les prix du produit \{#step-5-check-product-prices\} 1. Retournez dans la section **Monetization** → **Subscriptions** dans **App Store Connect**. 2. Cliquez sur le nom du groupe d'abonnements. 3. Sélectionnez le produit que vous testez. 4. Faites défiler jusqu'à **Subscription Pricing** et développez la section **Current Pricing for New Subscribers**. 5. Vérifiez que tous les prix requis sont bien listés. ## Étape 5. Vérifier le statut des applications payantes, le compte bancaire et les formulaires fiscaux 1. Sur la page d'accueil d'**[App Store Connect](https://appstoreconnect.apple.com/)**, cliquez sur **Business**. 2. Sélectionnez le nom de votre entreprise. 3. Faites défiler vers le bas et vérifiez que votre **Paid Apps Agreement**, votre **Bank Account** et vos **Tax forms** affichent tous le statut **Active**. En suivant ces étapes, vous devriez pouvoir résoudre l'avertissement `InvalidProductIdentifiers` et rendre vos produits disponibles dans le store. ## Étape 6. Recréer le produit s'il est bloqué Les étapes 1 à 5 peuvent toutes être validées — statut `Approved`, Bundle ID correspondant, clé API valide — et pourtant le SDK renvoie toujours `1000 noProductIDsFound`. Dans ce cas, le produit est peut-être bloqué dans le registre d'Apple. Il arrive que le registre des produits d'Apple entre dans un état où un produit existe dans l'interface d'App Store Connect mais n'est pas exposé au chemin de recherche StoreKit. Supprimez le produit dans App Store Connect et recréez-le avec le même identifiant de produit. Comptez jusqu'à 24 heures après la recréation pour la propagation. --- # File: cantMakePayments --- --- title: "Correction de l'erreur Code-1003 cantMakePayment" description: "Résolvez l'erreur de paiement lors de la gestion des abonnements dans Adapty." --- L'erreur 1003, `cantMakePayments`, indique que les achats intégrés ne peuvent pas être effectués sur cet appareil. Si vous rencontrez l'erreur `cantMakePayments`, cela est généralement dû à l'une des raisons suivantes : - Restrictions de l'appareil : L'erreur n'est pas liée à Adapty. Consultez les solutions ci-dessous. - Configuration du mode Observateur : La méthode `makePurchase` et le mode Observateur ne peuvent pas être utilisés simultanément. Consultez la section ci-dessous. ## Problème : Restrictions de l'appareil \{#issue-device-restrictions\} | Problème | Solution | |--------------------------------|-------------------------------------------------------------------------------------------------------------| | Restrictions Screen Time | Désactivez les restrictions d'achat intégré dans [Screen Time](https://support.apple.com/en-us/102470) | | Compte suspendu | Contactez le support Apple pour résoudre les problèmes de compte | | Restrictions régionales | Utilisez un compte App Store d'une région prise en charge | ## Problème : Utilisation simultanée du mode Observateur et de makePurchase \{#issue-using-both-observer-mode-and-makepurchase\} Si vous utilisez `makePurchases` pour gérer les achats, vous n'avez pas besoin d'utiliser le mode Observateur. Le [mode Observateur](observer-vs-full-mode) n'est nécessaire que si vous implémentez vous-même la logique d'achat. Ainsi, si vous utilisez `makePurchase`, vous pouvez supprimer en toute sécurité l'activation du mode Observateur dans le code d'initialisation du SDK. --- # File: ios-sdk-migration-guides --- --- title: "Guides de migration iOS SDK" description: "Guides de migration pour les versions du SDK Adapty iOS." --- Cette page regroupe tous les guides de migration pour le SDK Adapty iOS. Choisissez la version vers laquelle vous souhaitez migrer pour obtenir des instructions détaillées : - [**Migrer vers v4.0**](migration-to-ios-sdk-v4) - [**Migrer vers v3.15**](migration-to-ios-315) - **[Migrer vers v3.4](migration-to-ios-sdk-34)** - **[Migrer vers v3.3](migration-to-ios330)** - **[Migrer vers v3.0](migration-to-ios-sdk-v3)** --- # File: migration-to-ios-sdk-v4 --- --- title: "Migrer le SDK iOS Adapty vers la v4.0" description: "Migrez vers le SDK iOS Adapty v4.0 en remplaçant les API paywall par des API flow, compatibles avec le Flow Builder et le Paywall Builder." --- Le SDK iOS Adapty 4.0 introduit les flows et renomme les API paywall en conséquence. Les nouvelles API fonctionnent aussi bien avec le nouveau Flow Builder qu'avec le Paywall Builder existant — aucune modification de configuration n'est requise côté Adapty Dashboard. ## Référence rapide \{#quick-reference\} | v3 | v4 | |---|---| | `Adapty.getPaywall(placementId:locale:)` | `Adapty.getFlow(placementId:)` | | `AdaptyUI.getPaywallConfiguration(forPaywall:)` | `AdaptyUI.getFlowConfiguration(forFlow:locale:)` | | `Adapty.getPaywallProducts(paywall:)` | `Adapty.getPaywallProducts(flow:)` | | `Adapty.logShowPaywall(_:)` | `Adapty.logShowFlow(_:)` | | `AdaptyPaywallController` | `AdaptyFlowController` | | `AdaptyPaywallControllerDelegate` | `AdaptyFlowControllerDelegate` | | `AdaptyUI.paywallController(with:delegate:)` | `AdaptyUI.flowController(with:delegate:)` | | `.paywall()` (modificateur SwiftUI) | `.flow()` | | `AdaptyPaywallView` | `AdaptyFlowView` | | `didFailRenderingWith:` / `didFailRendering:` | `didReceiveError:` | | `didFinishPurchase` (optionnel, fermeture automatique en cas de succès) | `didFinishPurchase` (requis, pas de fermeture automatique) | | Produits de package `Adapty_KidsMode` / `AdaptyUI_KidsMode` | Trait de package `KidsMode` | | `Adapty.updateAttribution(_:source:)` (`source: String`) | `Adapty.updateAttribution(_:source:)` (`source: AdaptyAttributionSource`) | | `Adapty.setIntegrationIdentifier(key:value:)` | `Adapty.setIntegrationIdentifier(_:)` (`AdaptyIntegrationIdentifier`) | ## Version iOS minimale \{#minimum-ios-version\} Adapty iOS SDK 4.0 fait passer la cible de déploiement minimale d'iOS 13.0 à **iOS 15.0**. Définissez la cible de déploiement iOS de votre projet à 15.0 ou une version ultérieure avant de procéder à la mise à niveau. ## Installation : CocoaPods n'est plus pris en charge \{#installation-cocoapods-no-longer-supported\} Adapty iOS SDK 4.0 abandonne le support de CocoaPods. Installez le SDK avec [Swift Package Manager](sdk-installation-ios#install-adapty-sdk). Si votre projet utilise encore CocoaPods, supprimez les pods `Adapty` et `AdaptyUI` de votre `Podfile`, exécutez `pod install` pour les supprimer, puis ajoutez le package dans Xcode via **File → Add Package Dependency** en utilisant `https://github.com/adaptyteam/AdaptySDK-iOS.git`. ## Mode Enfants : produits séparés remplacés par un trait de package \{#kids-mode-separate-products-replaced-by-a-package-trait\} Dans la v3, vous activiez le [Mode Enfants](kids-mode) en sélectionnant les produits de package distincts **Adapty_KidsMode** et **AdaptyUI_KidsMode** et en renommant vos imports. Dans la v4.0, ces produits ont été supprimés. Le Mode Enfants est désormais un trait de package Swift nommé `KidsMode` sur le package Adapty standard — son activation exclut IDFA et AdSupport de l'ensemble du SDK à la compilation. Pour migrer : 1. Dans la fenêtre **Choose Package Products**, sélectionnez les produits standard **Adapty** et **AdaptyUI** au lieu de **Adapty_KidsMode** et **AdaptyUI_KidsMode**. 2. Activez le trait `KidsMode`. Dans Xcode 26.4 ou version ultérieure, activez-le pour la dépendance AdaptySDK-iOS dans la vue **Package Dependencies** de votre projet. Si vous ajoutez Adapty en tant que dépendance dans `Package.swift` (nécessite `swift-tools-version` 6.1 ou version ultérieure), activez-le à cet endroit : ```swift showLineNumbers title="Package.swift" .package( url: "https://github.com/adaptyteam/AdaptySDK-iOS.git", from: "4.0.2", traits: ["KidsMode"] ) ``` 3. Remettez vos imports sur les modules standard : ```diff showLineNumbers - import Adapty_KidsMode - import AdaptyUI_KidsMode + import Adapty + import AdaptyUI ``` :::note Les versions de Xcode antérieures à 26.4 ne permettent pas d'activer les traits pour un projet Xcode depuis l'interface. Dans ce cas, ajoutez un petit package Swift local qui dépend d'Adapty avec le trait `KidsMode` activé, et faites dépendre votre cible d'application de ce package. ::: ## APIs supprimées \{#removed-apis\} - **`Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:)`** — supprimée. Tous les produits incluent désormais les informations sur les offres, ce qui rend la vérification séparée d'éligibilité inutile. - **`AdaptyPaywallProductWithoutDeterminingOffer`** — supprimée. Les callbacks qui utilisaient auparavant ce type (comme `didSelectProduct`) transmettent maintenant `AdaptyPaywallProduct`. ## Les achats intégrés promus sur l'App Store temporairement supprimés \{#app-store-promoted-in-app-purchases-temporarily-removed\} Dans le cadre de la migration vers StoreKit 2, le SDK iOS Adapty 4.0 supprime la prise en charge des achats intégrés promus sur l'App Store. La méthode delegate `shouldAddStorePayment(for:)` et le type `AdaptyDeferredProduct` qu'elle reçoit ne sont pas disponibles dans la version 4.0. :::warning Cette suppression est temporaire — la prise en charge des achats intégrés promus sera de retour dans une version ultérieure 4.x. Si votre application repose sur des achats intégrés promus, restez sur le SDK iOS 3.x jusqu'au retour de cette fonctionnalité. ::: ## Récupération des paywalls \{#fetching-paywalls\} ### getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration Les types retournés passent de `AdaptyPaywall` / `AdaptyUI.PaywallConfiguration` à `AdaptyFlow` / `AdaptyUI.FlowConfiguration`. Le paramètre `locale` quitte l'appel de récupération et se déplace vers `getFlowConfiguration` : ```diff showLineNumbers - let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") - let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall) + let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID") + let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow, locale: "en") ``` ### getPaywallProducts(paywall:) → getPaywallProducts(flow:) `getPaywallProducts` prend désormais un `AdaptyFlow` retourné par `Adapty.getFlow` : ```diff showLineNumbers - let products = try await Adapty.getPaywallProducts(paywall: paywall) + let products = try await Adapty.getPaywallProducts(flow: flow) ``` ### Fichiers de secours \{#fallback-files\} Le format du fichier de secours [a changé avec le SDK v4](fallback-flows). Téléchargez le nouveau fichier depuis **[Placements](https://app.adapty.io/placements)** > **Fallbacks** et intégrez-le à votre application. ## Suivi des vues de paywall \{#tracking-paywall-views\} ### logShowPaywall(_:) → logShowFlow(_:) `logShowPaywall` est renommé en `logShowFlow` et prend désormais un `AdaptyFlow` à la place d'un `AdaptyPaywall`. L'événement est toujours enregistré pour la même variation, donc les métriques de funnel et de test A/B existantes continuent de fonctionner sans modification du tableau de bord. ```diff showLineNumbers - try await Adapty.logShowPaywall(paywall) + try await Adapty.logShowFlow(flow) ``` Comme dans la v3, vous n'avez pas besoin d'appeler cette méthode lors de l'affichage des flows ou des paywalls générés par le [Flow Builder](adapty-flow-builder) ou le [Paywall Builder](adapty-paywall-builder) — Adapty suit ces vues automatiquement. ## didFinishPurchase est maintenant obligatoire \{#didfinishpurchase-is-now-required\} Dans la v3, `didFinishPurchase` était facultatif : si vous ne l'implémentiez pas, le paywall se fermait automatiquement après un achat réussi. Dans la v4.0, ce comportement de fermeture automatique par défaut a été supprimé afin qu'un flow puisse continuer après un achat réussi — par exemple, pour afficher les écrans restants de votre flow. Vous décidez désormais ce qui se passe après un achat : fermer l'écran, ou ne rien faire pour laisser le flow continuer. - **UIKit** : les conformeurs à `AdaptyFlowControllerDelegate` doivent implémenter `didFinishPurchase` — cette méthode n'a plus d'implémentation par défaut. - **SwiftUI** : la closure `didFinishPurchase` de `.flow(...)` et `AdaptyFlowView(...)` est désormais non-optionnelle, au même titre que `didFailPurchase` et `didFinishRestore`. Pour conserver le comportement de la v3, fermez l'écran vous-même : ```swift showLineNumbers title="Swift" func flowController( _ controller: AdaptyFlowController, didFinishPurchase product: AdaptyPaywallProduct, purchaseResult: AdaptyPurchaseResult ) { if !purchaseResult.isPurchaseCancelled { controller.dismiss(animated: true) } } ``` ## UIKit \{#uikit\} ### AdaptyPaywallController → AdaptyFlowController Renommez le type de contrôleur et la méthode factory : ```diff showLineNumbers - let controller = try AdaptyUI.paywallController( - with: paywallConfiguration, - delegate: self - ) + let controller = try AdaptyUI.flowController( + with: flowConfiguration, + delegate: self + ) ``` ### AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate Renommez le protocole et mettez à jour chaque signature de méthode. Notez que `didSelectProduct` reçoit désormais `AdaptyPaywallProduct` au lieu de l'`AdaptyPaywallProductWithoutDeterminingOffer` supprimé, et `didFinishPurchase` [doit maintenant être implémenté](#didfinishpurchase-is-now-required) — il n'a plus d'implémentation par défaut. ```diff showLineNumbers - class YourClass: AdaptyPaywallControllerDelegate { + class YourClass: AdaptyFlowControllerDelegate { - func paywallControllerDidAppear(_ controller: AdaptyPaywallController) { } + func flowControllerDidAppear(_ controller: AdaptyFlowController) { } - func paywallControllerDidDisappear(_ controller: AdaptyPaywallController) { } + func flowControllerDidDisappear(_ controller: AdaptyFlowController) { } - func paywallController(_ controller: AdaptyPaywallController, - didPerform action: AdaptyUI.Action) { } + func flowController(_ controller: AdaptyFlowController, + didPerform action: AdaptyUI.Action) { } - func paywallController(_ controller: AdaptyPaywallController, - didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer) { } + func flowController(_ controller: AdaptyFlowController, + didSelectProduct product: AdaptyPaywallProduct) { } - func paywallController(_ controller: AdaptyPaywallController, - didStartPurchase product: AdaptyPaywallProduct) { } + func flowController(_ controller: AdaptyFlowController, + didStartPurchase product: AdaptyPaywallProduct) { } - func paywallController(_ controller: AdaptyPaywallController, - didFinishPurchase product: AdaptyPaywallProduct, - purchaseResult: AdaptyPurchaseResult) { } + func flowController(_ controller: AdaptyFlowController, + didFinishPurchase product: AdaptyPaywallProduct, + purchaseResult: AdaptyPurchaseResult) { } - func paywallController(_ controller: AdaptyPaywallController, - didFailPurchase product: AdaptyPaywallProduct, - error: AdaptyError) { } + func flowController(_ controller: AdaptyFlowController, + didFailPurchase product: AdaptyPaywallProduct, + error: AdaptyError) { } - func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { } + func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { } - func paywallController(_ controller: AdaptyPaywallController, - didFinishRestoreWith profile: AdaptyProfile) { } + func flowController(_ controller: AdaptyFlowController, + didFinishRestoreWith profile: AdaptyProfile) { } - func paywallController(_ controller: AdaptyPaywallController, - didFailRestoreWith error: AdaptyError) { } + func flowController(_ controller: AdaptyFlowController, + didFailRestoreWith error: AdaptyError) { } - func paywallController(_ controller: AdaptyPaywallController, - didFailRenderingWith error: AdaptyUIError) { } + func flowController(_ controller: AdaptyFlowController, + didReceiveError error: AdaptyUIError) { } - func paywallController(_ controller: AdaptyPaywallController, - didFailLoadingProductsWith error: AdaptyError) -> Bool { } + func flowController(_ controller: AdaptyFlowController, + didFailLoadingProductsWith error: AdaptyError) -> Bool { } - func paywallController(_ controller: AdaptyPaywallController, - didPartiallyLoadProducts failedIds: [String]) { } + func flowController(_ controller: AdaptyFlowController, + didPartiallyLoadProducts failedIds: [String]) { } - func paywallController(_ controller: AdaptyPaywallController, - didFinishWebPaymentNavigation product: AdaptyPaywallProduct?, - error: AdaptyError?) { } + func flowController(_ controller: AdaptyFlowController, + didFinishWebPaymentNavigation product: AdaptyPaywallProduct?, + error: AdaptyError?) { } } ``` ## SwiftUI \{#swiftui\} ### Modificateur `.paywall()` → `.flow()` \{#paywall-modifier--flow\} Renommez le modificateur, mettez à jour le nom du paramètre de configuration, et ajoutez la closure [`didFinishPurchase`](#didfinishpurchase-is-now-required) (désormais obligatoire) : ```diff showLineNumbers @State var flowPresented = false // rename freely — the variable name is your choice var body: some View { Text("Hello, AdaptyUI!") - .paywall( + .flow( isPresented: $flowPresented, - paywallConfiguration: paywallConfiguration, + flowConfiguration: flowConfiguration, + didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, - didFailRendering: { error in flowPresented = false } + didReceiveError: { error in flowPresented = false } ) } ``` Le callback renommé se déclenche pour les mêmes erreurs de rendu que `didFailRendering`, plus les nouvelles erreurs d'exécution provenant du script de flow (exceptions JavaScript avec le code `AdaptyUIError` `4105` — `.jsException`). Les corps de handler existants ne nécessitent aucune modification — il suffit de renommer le paramètre. ### AdaptyPaywallView → AdaptyFlowView Renommez la vue, mettez à jour le paramètre de configuration, ajoutez la closure [`didFinishPurchase`](#didfinishpurchase-is-now-required) (désormais obligatoire), et mettez à jour toute closure `didSelectProduct` — elle reçoit maintenant `AdaptyPaywallProduct` à la place du type supprimé `AdaptyPaywallProductWithoutDeterminingOffer` : ```diff showLineNumbers - AdaptyPaywallView( - paywallConfiguration: paywallConfiguration, - didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ }, + AdaptyFlowView( + flowConfiguration: flowConfiguration, + didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ }, + didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, - didFailRendering: { error in /* handle the error */ } + didReceiveError: { error in /* handle the error */ } ) ``` ## Ressources personnalisées AdaptyUI \{#adaptyui-custom-assets\} ### AdaptyUICustomVideoAsset Deux changements affectent tous les appels existants : - `.player` accepte désormais `AVPlayer` au lieu de `AVQueuePlayer`. - Chaque cas a reçu un paramètre supplémentaire `resolution: CGSize?` en fin de signature. Passez `nil` pour conserver le comportement actuel, ou indiquez la taille réelle en pixels afin que le lecteur puisse réserver l'espace de mise en page (ratio = `width / height`) avant le chargement de la vidéo. ```diff showLineNumbers - case file(url: URL, preview: AdaptyUICustomImageAsset?) - case remote(url: URL, preview: AdaptyUICustomImageAsset?) - case player(item: AVPlayerItem, player: AVQueuePlayer, preview: AdaptyUICustomImageAsset?) + case file(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?) + case remote(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?) + case player(item: AVPlayerItem, player: AVPlayer, preview: AdaptyUICustomImageAsset?, resolution: CGSize?) ``` ## Identifiants d'attribution et d'intégration \{#attribution-and-integration-identifiers\} ### updateAttribution(_:source:) Le paramètre `source` passe du type `String` au nouveau type `AdaptyAttributionSource`, et l'ancien `AdaptyProfile.AttributionSource` imbriqué est renommé en `AdaptyAttributionSource` au niveau supérieur. Utilisez l'une des sources prédéfinies, ou passez un littéral de chaîne pour toute autre source — `AdaptyAttributionSource` est conforme à `ExpressibleByStringLiteral`, donc les appels existants avec des littéraux de chaîne continuent de compiler. ```diff showLineNumbers - try await Adapty.updateAttribution(attribution, source: "adjust") + try await Adapty.updateAttribution(attribution, source: .adjust) ``` Sources prédéfinies : `.appleAds`, `.adjust`, `.appsflyer`, `.branch`, `.tenjin`. Si vous conservez la source dans une variable `String`, encapsulez-la : `AdaptyAttributionSource(rawValue: yourSource)`. ### setIntegrationIdentifier(_:) `setIntegrationIdentifier(key:value:)` est remplacé par une méthode variadique qui accepte une ou plusieurs valeurs `AdaptyIntegrationIdentifier`. Utilisez les méthodes factory prédéfinies plutôt que des clés de type chaîne brute : ```diff showLineNumbers - try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid) + try await Adapty.setIntegrationIdentifier(.appsflyerId(uid)) ``` Vous pouvez définir plusieurs identifiants en un seul appel : ```swift showLineNumbers try await Adapty.setIntegrationIdentifier( .appsflyerId(uid), .adjustDeviceId(adid) ) ``` Remplacez chaque ancienne chaîne de clé par sa méthode factory correspondante : | v3 key | v4 factory | |---|---| | `"adjust_device_id"` | `.adjustDeviceId(_:)` | | `"airbridge_device_id"` | `.airbridgeDeviceId(_:)` | | `"amplitude_user_id"` | `.amplitudeUserId(_:)` | | `"amplitude_device_id"` | `.amplitudeDeviceId(_:)` | | `"appmetrica_device_id"` | `.appmetricaDeviceId(_:)` | | `"appmetrica_profile_id"` | `.appmetricaProfileId(_:)` | | `"appsflyer_id"` | `.appsflyerId(_:)` | | `"branch_id"` | `.branchId(_:)` | | `"facebook_anonymous_id"` | `.facebookAnonymousId(_:)` | | `"firebase_app_instance_id"` | `.firebaseAppInstanceId(_:)` | | `"mixpanel_user_id"` | `.mixpanelUserId(_:)` | | `"one_signal_subscription_id"` | `.oneSignalSubscriptionId(_:)` | | `"one_signal_player_id"` | `.oneSignalPlayerId(_:)` | | `"posthog_distinct_user_id"` | `.posthogDistinctUserId(_:)` | | `"pushwoosh_hwid"` | `.pushwooshHWID(_:)` | | `"tenjin_analytics_installation_id"` | `.tenjinAnalyticsInstallationId(_:)` | --- # File: migration-to-ios-315 --- --- title: "Migrer le SDK iOS Adapty vers la v3.15" description: "Migrez vers le SDK iOS Adapty v3.15 pour de meilleures performances et de nouvelles fonctionnalités de monétisation." --- Si vous utilisez le [Paywall Builder](adapty-paywall-builder) en [mode Observer](observer-vs-full-mode), à partir du SDK iOS 3.15, vous devez implémenter une nouvelle méthode `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)`. Cette méthode offre un meilleur contrôle sur la logique de restauration, vous permettant de gérer les restaurations d'achats dans votre flow personnalisé. Pour tous les détails d'implémentation, consultez [Afficher les paywalls du Paywall Builder en mode Observer](ios-present-paywall-builder-paywalls-in-observer-mode). ```diff showLineNumbers func observerMode(didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void) { // use the product object to handle the purchase // use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase } + func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void, + onFinishRestore: @escaping () -> Void) { + // use the onStartRestore and onFinishRestore callbacks to notify AdaptyUI about the process of the restore + } ``` --- # File: migration-to-ios-sdk-34 --- --- title: "Migrer vers le SDK Adapty iOS v3.4" description: "Migrez vers le SDK Adapty iOS v3.4 pour de meilleures performances et de nouvelles fonctionnalités de monétisation." --- Le SDK Adapty 3.4.0 est une version majeure qui introduit des améliorations nécessitant des étapes de migration de votre côté. ## Mettre à jour l'activation du SDK Adapty \{#update-adapty-sdk-activation\} ```diff showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") - Adapty.activate(with: configurationBuilder) { error in + Adapty.activate(with: configurationBuilder.build()) { error in // handle the error } ``` **Mettre à jour les fichiers de paywall de secours** Mettez à jour vos fichiers de paywall de secours pour garantir la compatibilité avec la nouvelle version du SDK : 1. [Téléchargez les fichiers de paywall de secours mis à jour](fallback-paywalls) depuis l'Adapty Dashboard. 2. [Remplacez les paywalls de secours existants dans votre application mobile](ios-use-fallback-paywalls) par les nouveaux fichiers. ```diff showLineNumbers @main struct SampleApp: App { init() { let configurationBuilder = AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") Task { - try await Adapty.activate(with: configurationBuilder) + try await Adapty.activate(with: configurationBuilder.build()) } } var body: some Scene { WindowGroup { ContentView() } } } ``` **Mettre à jour les fichiers de paywall de secours** Mettez à jour vos fichiers de paywall de secours pour garantir la compatibilité avec la nouvelle version du SDK : 1. [Téléchargez les fichiers de paywall de secours mis à jour](fallback-paywalls) depuis l'Adapty Dashboard. 2. [Remplacez les paywalls de secours existants dans votre application mobile](ios-use-fallback-paywalls) par les nouveaux fichiers. --- # File: migration-to-ios330 --- --- title: "Migrer le SDK iOS Adapty vers v3.3" description: "Migrez vers le SDK iOS Adapty v3.3 pour de meilleures performances et de nouvelles fonctionnalités de monétisation." --- Le SDK Adapty 3.3.0 est une version majeure qui apporte des améliorations pouvant nécessiter quelques étapes de migration de votre part. 1. Renommer `Adapty.Configuration` en `AdaptyConfiguration`. 2. Renommer la méthode `getViewConfiguration` en `getPaywallConfiguration`. 3. Supprimer les paramètres `didCancelPurchase` et `paywall` de SwiftUI, et renommer le paramètre `viewConfiguration` en `paywallConfiguration`. 4. Mettre à jour la gestion des achats intégrés promotionnels depuis l'App Store en supprimant le paramètre `defermentCompletion` de la méthode `AdaptyDelegate`. 5. Supprimer la méthode `getProductsIntroductoryOfferEligibility`. 6. Mettre à jour les configurations d'intégration pour Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase et Google Analytics, Mixpanel, OneSignal, Pushwoosh. 7. Mettre à jour l'implémentation du mode Observer.
## Renommer Adapty.Configuration en AdaptyConfiguration \{#rename-adaptyconfiguration-to-adaptyconfiguration\} Mettez à jour le code d'activation du SDK iOS Adapty de la façon suivante : ```diff showLineNumbers // In your AppDelegate class: let configurationBuilder = - Adapty.Configuration + AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) .with(customerUserId: "YOUR_USER_ID") .with(idfaCollectionDisabled: false) .with(ipAddressCollectionDisabled: false) Adapty.activate(with: configurationBuilder) { error in // handle the error } ``` ```diff showLineNumbers @main struct SampleApp: App { init() let configurationBuilder = - Adapty.Configuration + AdaptyConfiguration .builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) // optional .with(customerUserId: "YOUR_USER_ID") // optional .with(idfaCollectionDisabled: false) // optional .with(ipAddressCollectionDisabled: false) // optional Task { try await Adapty.activate(with: configurationBuilder) } } var body: some Scene { WindowGroup { ContentView() } } } ``` ## Renommer la méthode getViewConfiguration en getPaywallConfiguration \{#rename-getviewconfiguration-method-to-getpaywallconfiguration\} Mettez à jour le nom de la méthode pour récupérer la `viewConfiguration` du paywall : ```diff showLineNumbers guard paywall.hasViewConfiguration else { // use your custom logic return } do { - let paywallConfiguration = try await AdaptyUI.getViewConfiguration( + let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall ) // use loaded configuration } catch { // handle the error } ``` Pour plus de détails sur la méthode, consultez [Récupérer la configuration de vue d'un paywall conçu avec le Paywall Builder](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). ## Modifier les paramètres dans SwiftUI \{#change-parameters-in-swiftui\} Les mises à jour suivantes ont été apportées à SwiftUI : 1. Le paramètre `didCancelPurchase` a été supprimé. Utilisez `didFinishPurchase` à la place. 2. La méthode `.paywall()` n'accepte plus d'objet paywall. 3. Le paramètre `paywallConfiguration` remplace le paramètre `viewConfiguration`. Mettez à jour votre code comme ceci : ```diff showLineNumbers @State var paywallPresented = false var body: some View { Text("Hello, AdaptyUI!") .paywall( isPresented: $paywallPresented, - paywall: , - viewConfiguration: , + paywallConfiguration: , didPerformAction: { action in switch action { case .close: paywallPresented = false default: // Handle other actions break } }, - didFinishPurchase: { product, profile in paywallPresented = false }, + didFinishPurchase: { product, purchaseResult in /* handle the result*/ }, didFailPurchase: { product, error in /* handle the error */ }, didFinishRestore: { profile in /* check access level and dismiss */ }, didFailRestore: { error in /* handle the error */ }, didFailRendering: { error in paywallPresented = false } - didCancelPurchase: { product in /* handle the result*/} ) } ``` ## Mettre à jour la gestion des achats intégrés promotionnels depuis l'App Store \{#update-handling-of-promotional-in-app-purchases-from-app-store\} Mettez à jour la façon dont vous gérez les achats intégrés promotionnels depuis l'App Store en supprimant le paramètre `defermentCompletion` de la méthode `AdaptyDelegate`, comme indiqué dans l'exemple ci-dessous : ```swift showLineNumbers title="Swift" final class YourAdaptyDelegateImplementation: AdaptyDelegate { nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool { // 1a. // Return `true` to continue the transaction in your app. // 1b. // Store the product object and return `false` to defer or cancel the transaction. false } // 2. Continue the deferred purchase later on by passing the product to `makePurchase` func continueDeferredPurchase() async { let storedProduct: AdaptyDeferredProduct = // get the product object from the 1b. do { try await Adapty.makePurchase(product: storedProduct) } catch { // handle the error } } } ``` ## Supprimer la méthode getProductsIntroductoryOfferEligibility \{#remove-getproductsintroductoryoffereligibility-method\} Avant le SDK iOS Adapty 3.3.0, l'objet produit incluait toujours les offres, peu importe si l'utilisateur y était éligible. Vous deviez vérifier manuellement l'éligibilité avant d'utiliser l'offre. Désormais, l'objet produit n'inclut une offre que si l'utilisateur est éligible. Cela signifie que vous n'avez plus besoin de vérifier l'éligibilité — si une offre est présente, l'utilisateur y est éligible. Si vous souhaitez tout de même consulter les offres pour les utilisateurs non éligibles, référez-vous à `sk1Product` et `sk2Product`. ## Mettre à jour la configuration des SDK d'intégrations tierces \{#update-third-party-integration-sdk-configuration\} À partir du SDK iOS Adapty 3.3.0, nous avons mis à jour l'API publique de la méthode `updateAttribution`. Auparavant, elle acceptait un dictionnaire `[AnyHashable: Any]`, vous permettant de passer directement des objets d'attribution issus de différents services. Désormais, elle requiert un `[String: any Sendable]`, vous devrez donc convertir les objets d'attribution avant de les passer. Pour garantir le bon fonctionnement des intégrations avec le SDK iOS Adapty 3.3.0 et les versions ultérieures, mettez à jour vos configurations SDK pour les intégrations suivantes comme décrit dans les sections ci-dessous. ### Adjust \{#adjust\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Adjust](adjust#connect-your-app-to-adjust). ```diff showLineNumbers class AdjustModuleImplementation { - func updateAdjustAttribution() { - Adjust.attribution { attribution in - guard let attributionDictionary = attribution?.dictionary()?.toSendableDict() else { return } - - Adjust.adid { adid in - guard let adid else { return } - - Adapty.updateAttribution(attributionDictionary, source: .adjust, networkUserId: adid) { error in - // handle the error - } - } - } - } + func updateAdjustAdid() { + Adjust.adid { adid in + guard let adid else { return } + + Adapty.setIntegrationIdentifier(key: "adjust_device_id", value: adid) + } + } + + func updateAdjustAttribution() { + Adjust.attribution { attribution in + guard let attribution = attribution?.dictionary() else { + return + } + + Adapty.updateAttribution(attribution, source: "adjust") + } + } } ``` ```diff showLineNumbers class YourAdjustDelegateImplementation { // Find your implementation of AdjustDelegate // and update adjustAttributionChanged method: func adjustAttributionChanged(_ attribution: ADJAttribution?) { - if let attribution = attribution?.dictionary()?.toSendableDict() { - Adapty.updateAttribution(attribution, source: .adjust) + if let attribution = attribution?.dictionary() { + Adapty.updateAttribution(attribution, source: "adjust") } } } ``` ### AirBridge \{#airbridge\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration AirBridge](airbridge#connect-your-app-to-airbridge). ```diff showLineNumbers import AirBridge - let builder = AdaptyProfileParameters.Builder() - .with(airbridgeDeviceId: AirBridge.deviceUUID()) - - Adapty.updateProfile(params: builder.build()) + do { + try await Adapty.setIntegrationIdentifier( + key: "airbridge_device_id", + value: AirBridge.deviceUUID() + ) + } catch { + // handle the error + } ``` ### Amplitude \{#amplitude\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Amplitude](amplitude#sdk-configuration). ```diff showLineNumbers import Amplitude - let builder = AdaptyProfileParameters.Builder() - .with(amplitudeUserId: Amplitude.instance().userId) - .with(amplitudeDeviceId: Amplitude.instance().deviceId) - - Adapty.updateProfile(params: builder.build()) + do { + try await Adapty.setIntegrationIdentifier( + key: "amplitude_user_id", + value: Amplitude.instance().userId + ) + try await Adapty.setIntegrationIdentifier( + key: "amplitude_device_id", + value: Amplitude.instance().deviceId + ) + } catch { + // handle the error + } ``` ### AppMetrica \{#appmetrica\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration AppMetrica](appmetrica#sdk-configuration). ```diff showLineNumbers import AppMetricaCore - if let deviceID = AppMetrica.deviceID { - let builder = AdaptyProfileParameters.Builder() - .with(appmetricaDeviceId: deviceID) - .with(appmetricaProfileId: "YOUR_ADAPTY_CUSTOMER_USER_ID") - - Adapty.updateProfile(params: builder.build()) - } + if let deviceID = AppMetrica.deviceID { + do { + try await Adapty.setIntegrationIdentifier( + key: "appmetrica_device_id", + value: deviceID + ) + try await Adapty.setIntegrationIdentifier( + key: "appmetrica_profile_id", + value: "YOUR_ADAPTY_CUSTOMER_USER_ID" + ) + } catch { + // handle the error + } + } ``` ### AppsFlyer \{#appsflyer\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration AppsFlyer](appsflyer#connect-your-app-to-appsflyer). ```diff showLineNumbers class YourAppsFlyerLibDelegateImplementation { // Find your implementation of AppsFlyerLibDelegate // and update onConversionDataSuccess method: func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) { let uid = AppsFlyerLib.shared().getAppsFlyerUID() - Adapty.updateAttribution( - conversionInfo.toSendableDict(), - source: .appsflyer, - networkUserId: uid - ) + Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid) + Adapty.updateAttribution(conversionInfo, source: "appsflyer") } } ``` ### Branch \{#branch\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Branch](branch#connect-your-app-to-branch). ```diff showLineNumbers class YourBranchImplementation { func initializeBranch() { // Pass the attribution you receive from the initializing method of Branch iOS SDK to Adapty. Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in - if let data = data?.toSendableDict() { - Adapty.updateAttribution(data, source: .branch) - } + if let data { + Adapty.updateAttribution(data, source: "branch") + } } } } ``` ### Facebook Ads \{#facebook-ads\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads). ```diff showLineNumbers import FacebookCore - let builder = AdaptyProfileParameters.Builder() - .with(facebookAnonymousId: AppEvents.shared.anonymousID) - - do { - try Adapty.updateProfile(params: builder.build()) - } catch { - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "facebook_anonymous_id", + value: AppEvents.shared.anonymousID + ) + } catch { + // handle the error + } ``` ### Firebase et Google Analytics \{#firebase-and-google-analytics\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Firebase et Google Analytics](firebase-and-google-analytics). ```diff showLineNumbers import FirebaseCore import FirebaseAnalytics FirebaseApp.configure() - if let appInstanceId = Analytics.appInstanceID() { - let builder = AdaptyProfileParameters.Builder() - .with(firebaseAppInstanceId: appInstanceId) - Adapty.updateProfile(params: builder.build()) { error in - // handle error - } - } + if let appInstanceId = Analytics.appInstanceID() { + do { + try await Adapty.setIntegrationIdentifier( + key: "firebase_app_instance_id", + value: appInstanceId + ) + } catch { + // handle the error + } + } ``` ### Mixpanel \{#mixpanel\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Mixpanel](mixpanel#sdk-configuration). ```diff showLineNumbers import Mixpanel - let builder = AdaptyProfileParameters.Builder() - .with(mixpanelUserId: Mixpanel.mainInstance().distinctId) - - do { - try await Adapty.updateProfile(params: builder.build()) - } catch { - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "mixpanel_user_id", + value: Mixpanel.mainInstance().distinctId + ) + } catch { + // handle the error + } ``` ### OneSignal \{#onesignal\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration OneSignal](onesignal#sdk-configuration). ```diff showLineNumbers // PlayerID (pre-v5 OneSignal SDK) // in your OSSubscriptionObserver implementation func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) { if let playerId = stateChanges.to.userId { - let params = AdaptyProfileParameters.Builder() - .with(oneSignalPlayerId: playerId) - .build() - - Adapty.updateProfile(params:params) { error in - // check error - } + Task { + try await Adapty.setIntegrationIdentifier( + key: "one_signal_player_id", + value: playerId + ) + } } } // SubscriptionID (v5+ OneSignal SDK) OneSignal.Notifications.requestPermission({ accepted in - let id = OneSignal.User.pushSubscription.id - - let builder = AdaptyProfileParameters.Builder() - .with(oneSignalSubscriptionId: id) - - Adapty.updateProfile(params: builder.build()) + Task { + try await Adapty.setIntegrationIdentifier( + key: "one_signal_subscription_id", + value: OneSignal.User.pushSubscription.id + ) + } }, fallbackToSettings: true) ``` ### Pushwoosh \{#pushwoosh\} Mettez à jour le code de votre application mobile comme indiqué ci-dessous. Pour l'exemple de code complet, consultez la [configuration du SDK pour l'intégration Pushwoosh](pushwoosh#sdk-configuration). ```diff showLineNumbers - let params = AdaptyProfileParameters.Builder() - .with(pushwooshHWID: Pushwoosh.sharedInstance().getHWID()) - .build() - - Adapty.updateProfile(params: params) { error in - // handle the error - } + do { + try await Adapty.setIntegrationIdentifier( + key: "pushwoosh_hwid", + value: Pushwoosh.sharedInstance().getHWID() + ) + } catch { + // handle the error + } ``` ## Mettre à jour l'implémentation du mode Observer \{#update-observer-mode-implementation\} Mettez à jour la façon dont vous liez les paywalls aux transactions. Auparavant, vous utilisiez la méthode `setVariationId` pour assigner le `variationId`. Désormais, vous pouvez inclure le `variationId` directement lors de l'enregistrement de la transaction via la nouvelle méthode `reportTransaction`. Consultez l'exemple de code final dans [Associer des paywalls à des transactions d'achat en mode Observer](report-transactions-observer-mode). :::warning N'oubliez pas d'enregistrer la transaction via la méthode `reportTransaction`. Si vous omettez cette étape, Adapty ne reconnaîtra pas la transaction, n'accordera pas les niveaux d'accès, ne l'inclura pas dans les analytics et ne l'enverra pas aux intégrations. Cette étape est indispensable ! ::: ```diff showLineNumbers - let variationId = paywall.variationId - - // There are two overloads: for StoreKit 1 and StoreKit 2 - Adapty.setVariationId(variationId, forPurchasedTransaction: transaction) { error in - if error == nil { - // successful binding - } - } + do { + // every time when calling transaction.finish() + try await Adapty.reportTransaction(transaction, withVariationId: ) + } catch { + // handle the error + } ``` --- # File: migration-to-ios-sdk-v3 --- --- title: "Migrer le SDK Adapty iOS vers v3.0" description: "Migrez vers le SDK Adapty iOS v3.0 pour de meilleures performances et de nouvelles fonctionnalités de monétisation." --- Le SDK Adapty v3.0 apporte la prise en charge du nouveau [Adapty Paywall Builder](adapty-paywall-builder), la nouvelle version de l'outil no-code convivial pour créer des paywalls. Grâce à sa flexibilité maximale et à ses riches capacités de design, vos paywalls seront plus efficaces et rentables que jamais. :::info Veuillez noter que la bibliothèque AdaptyUI est dépréciée et fait désormais partie intégrante d'AdaptySDK. ::: ## Réinstaller le SDK Adapty v3.x via Swift Package Manager \{#reinstall-adapty-sdk-v3x-via-swift-package-manager\} 1. Supprimez la dépendance au package AdaptyUI de votre projet, vous n'en aurez plus besoin. 2. Même si vous l'avez déjà, vous devrez ré-ajouter la dépendance au SDK Adapty. Pour cela, dans Xcode, ouvrez **File** -> **Add Package Dependency...**. Notez que la façon d'ajouter des dépendances de packages peut varier selon les versions de XCode. Consultez la documentation XCode si nécessaire. 3. Entrez l'URL du dépôt `https://github.com/adaptyteam/AdaptySDK-iOS.git` 4. Choisissez la version, puis cliquez sur le bouton **Add package**. 5. Choisissez les modules dont vous avez besoin : 1. **Adapty** est le module obligatoire 2. **AdaptyUI** est un module optionnel nécessaire si vous prévoyez d'utiliser le [Adapty Paywall Builder](adapty-paywall-builder). 6. Xcode ajoutera la dépendance au package à votre projet, et vous pourrez l'importer. Pour cela, dans la fenêtre **Choose Package Products**, cliquez à nouveau sur le bouton **Add package**. Le package apparaîtra dans la liste **Packages**. ## Réinstaller le SDK Adapty v3.x via CocoaPods \{#reinstall-adapty-sdk-v3x-via-cocoapods\} 1. Ajoutez Adapty à votre `Podfile`. Choisissez les modules dont vous avez besoin : 1. **Adapty** est le module obligatoire. 2. **AdaptyUI** est un module optionnel nécessaire si vous prévoyez d'utiliser le [Adapty Paywall Builder](adapty-paywall-builder). 2. ```shell showLineNumbers title="Podfile" pod 'Adapty', '~> 3.2.0' pod 'AdaptyUI', '~> 3.2.0' # optional module needed only for Paywall Builder ``` 3. Exécutez : ```sh showLineNumbers title="Shell" pod install ``` Cela crée un fichier `.xcworkspace` pour votre application. Utilisez ce fichier pour tout le développement futur de votre application. Activez les modules SDK Adapty et AdaptyUI. Avant la v3.0, vous n'activiez pas AdaptyUI — pensez à **ajouter l'activation d'AdaptyUI**. Les paramètres ne changent pas, conservez-les tels quels. ```swift showLineNumbers // In your AppDelegate class: let configurationBuilder = AdaptyConfiguration .Builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) .with(customerUserId: "YOUR_USER_ID") .with(idfaCollectionDisabled: false) .with(ipAddressCollectionDisabled: false) Adapty.activate(with: configurationBuilder) { error in // handle the error } // Only if you are going to use AdaptyUI AdaptyUI.activate() ``` ```swift title="" showLineNumbers @main struct SampleApp: App { init() let configurationBuilder = AdaptyConfiguration .Builder(withAPIKey: "PUBLIC_SDK_KEY") .with(observerMode: false) // optional .with(customerUserId: "YOUR_USER_ID") // optional .with(idfaCollectionDisabled: false) // optional .with(ipAddressCollectionDisabled: false) // optional Adapty.activate(with: configurationBuilder) { error in // handle the error } // Only if you are going to use AdaptyUI AdaptyUI.activate() } var body: some Scene { WindowGroup { ContentView() } } } ``` --- # End of Documentation _Generated on: 2026-08-11T07:13:23.506Z_ _Successfully processed: 53/53 files_