---
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.0",
       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(_:)` |