### Instantiate AdaptyPaywallController Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Use this method to create a paywall controller. You can optionally provide pre-fetched products and a tag resolver for custom text. Ensure the controller's presentation style is set before presenting. ```swift import Adapty import AdaptyUI let controller = AdaptyUI.paywallController( for: paywall, products: nil, // Pass pre-fetched products or nil to let AdaptyUI fetch them viewConfiguration: viewConfiguration, delegate: self, // AdaptyPaywallControllerDelegate observerModeDelegate: nil, tagResolver: [ "USERNAME": "John", "PLAN_NAME": "Premium" ] ) controller.modalPresentationStyle = .overCurrentContext present(controller, animated: true) ``` -------------------------------- ### Fetch View Configuration using async/await Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Load the visual configuration for a paywall using async/await syntax. This method uses a default load timeout. ```swift // async/await (iOS 13+) do { let viewConfiguration = try await AdaptyUI.getViewConfiguration(forPaywall: paywall) print("Template: \(viewConfiguration.templateId)") } catch { print("View config error: \(error)") } ``` -------------------------------- ### Fetch View Configuration using Callback Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Load the visual configuration for a paywall using a callback. A default load timeout of 5 seconds is applied. ```swift import Adapty import AdaptyUI // Callback-based AdaptyUI.getViewConfiguration(forPaywall: paywall, loadTimeout: 5.0) { result in switch result { case let .success(viewConfiguration): print("Template: \(viewConfiguration.templateId)") // Proceed to create the paywall controller case let .failure(error): print("Failed to load view configuration: \(error.localizedDescription)") } } ``` -------------------------------- ### Adapty SDK Initialization Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Initializes the Adapty SDK on app launch. This must be called before any other Adapty or AdaptyUI SDK methods. ```APIDOC ## Adapty.activate(_:) ### Description Initializes the Adapty SDK on app launch. AdaptyUI relies on an active Adapty session. ### Method `Adapty.activate(_: "YOUR_API_KEY")` ### Parameters - **"YOUR_API_KEY"** (string) - Required - Your Adapty public SDK key. ### Request Example ```swift import Adapty import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { Adapty.logLevel = .verbose // Optional: enable detailed logs during development Adapty.activate("YOUR_API_KEY") // Replace with your Adapty public SDK key return true } } ``` ``` -------------------------------- ### Load View Configuration with AdaptyUI Source: https://github.com/adaptyteam/adaptyui-ios/blob/main/README.md After fetching a paywall, use this method to load the associated view configuration. This is necessary before presenting the paywall visually. Handles success by providing the view configuration or failure by returning an error. ```swift import Adapty AdaptyUI.getViewConfiguration(paywall: paywall, locale: "en") { result in switch result { case let .success(viewConfiguration): // use loaded configuration case let .failure(error): // handle the error } } ``` -------------------------------- ### Instantiate Paywall Controller Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Creates an AdaptyPaywallController (UIViewController subclass) ready to be presented. Optionally supply pre-fetched AdaptyPaywallProduct items to skip a secondary network request, and a tagResolver to substitute custom text placeholders. ```APIDOC ## `AdaptyUI.paywallController(for:products:viewConfiguration:delegate:tagResolver:)` — Instantiate the paywall screen Creates an `AdaptyPaywallController` (`UIViewController` subclass) ready to be presented. Optionally supply pre-fetched `AdaptyPaywallProduct` items to skip a secondary network request, and a `tagResolver` to substitute custom text placeholders. ```swift import Adapty import AdaptyUI let controller = AdaptyUI.paywallController( for: paywall, products: nil, // Pass pre-fetched products or nil to let AdaptyUI fetch them viewConfiguration: viewConfiguration, delegate: self, // AdaptyPaywallControllerDelegate observerModeDelegate: nil, tagResolver: [ "USERNAME": "John", "PLAN_NAME": "Premium" ] ) controller.modalPresentationStyle = .overCurrentContext present(controller, animated: true) ``` ``` -------------------------------- ### Create Adapty Paywall Controller Source: https://github.com/adaptyteam/adaptyui-ios/blob/main/README.md This method creates a view controller for displaying a visual paywall. It requires the paywall object, its associated products, the loaded view configuration, and a delegate for handling interactions. ```swift import Adapty import AdaptyUI let visualPaywall = AdaptyUI.paywallController( for: , products: , viewConfiguration: , delegate: ) ``` -------------------------------- ### AdaptyPaywallControllerDelegate Methods Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Implement these methods to respond to user actions, purchase events, restore flows, and rendering errors. Default implementations are provided for optional methods. ```swift import Adapty import AdaptyUI import UIKit class MyViewController: UIViewController, AdaptyPaywallControllerDelegate { // MARK: – User actions func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .close: controller.dismiss(animated: true) case let .openURL(url): UIApplication.shared.open(url, options: [:]) case let .custom(id): print("Custom action: \(id)") // e.g., trigger login flow } } func paywallController(_ controller: AdaptyPaywallController, didSelectProduct product: AdaptyPaywallProduct) { print("Selected: \(product.vendorProductId)") } // MARK: – Purchase flow func paywallController(_ controller: AdaptyPaywallController, didStartPurchase product: AdaptyPaywallProduct) { print("Purchase started for: \(product.vendorProductId)") } func paywallController(_ controller: AdaptyPaywallController, didFinishPurchase product: AdaptyPaywallProduct, purchasedInfo: AdaptyPurchasedInfo) { // Default implementation dismisses the controller controller.dismiss(animated: true) } func paywallController(_ controller: AdaptyPaywallController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError) { print("Purchase failed: \(error.localizedDescription)") } func paywallController(_ controller: AdaptyPaywallController, didCancelPurchase product: AdaptyPaywallProduct) { print("Purchase cancelled by user") } // MARK: – Restore flow func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { print("Restore started") } func paywallController(_ controller: AdaptyPaywallController, didFinishRestoreWith profile: AdaptyProfile) { if profile.accessLevels["premium"]?.isActive ?? false { controller.dismiss(animated: true) } } func paywallController(_ controller: AdaptyPaywallController, didFailRestoreWith error: AdaptyError) { print("Restore failed: \(error.localizedDescription)") } // MARK: – Errors func paywallController(_ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError) { print("Rendering error: \(error.localizedDescription)") DispatchQueue.main.asyncAfter(deadline: .now() + 1) { controller.dismiss(animated: true) } } func paywallController(_ controller: AdaptyPaywallController, didFailLoadingProductsWith error: AdaptyError) -> Bool { print("Products failed to load: \(error.localizedDescription)") return false // Return true to retry product fetching } } ``` -------------------------------- ### Fetching the View Configuration Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Loads the visual configuration for a given paywall, which describes its layout and elements. ```APIDOC ## AdaptyUI.getViewConfiguration(forPaywall:loadTimeout:) ### Description Fetches the `AdaptyUI.LocalizedViewConfiguration` that describes the paywall's visual layout. A `loadTimeout` is applied by default; cached or fallback content is returned on timeout. ### Method `AdaptyUI.getViewConfiguration(forPaywall: paywall, loadTimeout: 5.0)` ### Parameters - **paywall** (`AdaptyPaywall`) - Required - The paywall object obtained from `Adapty.getPaywall`. - **loadTimeout** (Double) - Optional - The timeout in seconds for loading the configuration. Defaults to 5.0 seconds. ### Response #### Success Response - **viewConfiguration** (`AdaptyUI.LocalizedViewConfiguration`) - An object describing the paywall's visual layout. - **templateId** (string) - The ID of the template used for the paywall. ### Request Example (Callback-based) ```swift import Adapty import AdaptyUI AdaptyUI.getViewConfiguration(forPaywall: paywall, loadTimeout: 5.0) { result in switch result { case let .success(viewConfiguration): print("Template: \(viewConfiguration.templateId)") // Proceed to create the paywall controller case let .failure(error): print("Failed to load view configuration: \(error.localizedDescription)") } } ``` ### Request Example (async/await) ```swift do { let viewConfiguration = try await AdaptyUI.getViewConfiguration(forPaywall: paywall) print("Template: \(viewConfiguration.templateId)") } catch { print("View config error: \(error)") } ``` ``` -------------------------------- ### AdaptyPaywallControllerDelegate Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Implement this protocol to handle purchase outcomes, restore flows, user actions, and rendering errors. All methods with a `default` implementation are optional to override. ```APIDOC ## AdaptyPaywallControllerDelegate ### `AdaptyPaywallControllerDelegate` — Respond to paywall lifecycle events Implement this protocol to handle purchase outcomes, restore flows, user actions, and rendering errors. All methods with a `default` implementation are optional to override. ```swift import Adapty import AdaptyUI import UIKit class MyViewController: UIViewController, AdaptyPaywallControllerDelegate { // MARK: – User actions func paywallController(_ controller: AdaptyPaywallController, didPerform action: AdaptyUI.Action) { switch action { case .close: controller.dismiss(animated: true) case let .openURL(url): UIApplication.shared.open(url, options: [:]) case let .custom(id): print("Custom action: \(id)") // e.g., trigger login flow } } func paywallController(_ controller: AdaptyPaywallController, didSelectProduct product: AdaptyPaywallProduct) { print("Selected: \(product.vendorProductId)") } // MARK: – Purchase flow func paywallController(_ controller: AdaptyPaywallController, didStartPurchase product: AdaptyPaywallProduct) { print("Purchase started for: \(product.vendorProductId)") } func paywallController(_ controller: AdaptyPaywallController, didFinishPurchase product: AdaptyPaywallProduct, purchasedInfo: AdaptyPurchasedInfo) { // Default implementation dismisses the controller controller.dismiss(animated: true) } func paywallController(_ controller: AdaptyPaywallController, didFailPurchase product: AdaptyPaywallProduct, error: AdaptyError) { print("Purchase failed: \(error.localizedDescription)") } func paywallController(_ controller: AdaptyPaywallController, didCancelPurchase product: AdaptyPaywallProduct) { print("Purchase cancelled by user") } // MARK: – Restore flow func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { print("Restore started") } func paywallController(_ controller: AdaptyPaywallController, didFinishRestoreWith profile: AdaptyProfile) { if profile.accessLevels["premium"]?.isActive ?? false { controller.dismiss(animated: true) } } func paywallController(_ controller: AdaptyPaywallController, didFailRestoreWith error: AdaptyError) { print("Restore failed: \(error.localizedDescription)") } // MARK: – Errors func paywallController(_ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError) { print("Rendering error: \(error.localizedDescription)") DispatchQueue.main.asyncAfter(deadline: .now() + 1) { controller.dismiss(animated: true) } } func paywallController(_ controller: AdaptyPaywallController, didFailLoadingProductsWith error: AdaptyError) -> Bool { print("Products failed to load: \(error.localizedDescription)") return false // Return true to retry product fetching } } ``` ``` -------------------------------- ### Initialize Adapty SDK in AppDelegate Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Activate the Adapty SDK with your public API key in the application's launch method. This must be called before any other Adapty SDK usage. ```swift import Adapty import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { Adapty.logLevel = .verbose // Optional: enable detailed logs during development Adapty.activate("YOUR_API_KEY") // Replace with your Adapty public SDK key return true } } ``` -------------------------------- ### Implement AdaptyObserverModeDelegate for Custom Purchase Flows Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Use this delegate when your app manages its own purchase flow. Implement `onStartPurchase` and `onFinishPurchase` to control the UI loading state. Ensure you wire up the `observerModeDelegate` when creating the `AdaptyPaywallController`. ```swift import Adapty import AdaptyUI import UIKit class PurchaseManager: NSObject, AdaptyObserverModeDelegate { func paywallController( _ controller: AdaptyPaywallController, didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void ) { onStartPurchase() // Show loading state in the paywall UI MyCustomPurchaseService.buy(product.vendorProductId) { success in onFinishPurchase() // Hide loading state if success { controller.dismiss(animated: true) } } } } // Wire up observer mode delegate when creating the controller let controller = AdaptyUI.paywallController( for: paywall, products: nil, viewConfiguration: viewConfig, delegate: myDelegate, observerModeDelegate: PurchaseManager() ) present(controller, animated: true) ``` -------------------------------- ### Fetch Paywall using async/await Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Load an AdaptyPaywall object using its placement ID with async/await syntax. Requires iOS 13+. ```swift // async/await (iOS 13+) do { let paywall = try await Adapty.getPaywall(placementId: "your_placement_id") print("Loaded paywall: \(paywall.variationId)") } catch { print("Failed: \(error)") } ``` -------------------------------- ### Fetching a Paywall Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Loads a paywall object from the Adapty backend using its placement ID. This is a prerequisite for fetching the visual configuration. ```APIDOC ## Adapty.getPaywall(placementId:) ### Description Fetches an `AdaptyPaywall` object from the Adapty backend. The placement ID is configured in the Adapty dashboard. ### Method `Adapty.getPaywall(placementId: "your_placement_id")` ### Parameters - **placementId** (string) - Required - The placement ID configured in the Adapty dashboard. ### Response #### Success Response - **paywall** (`AdaptyPaywall`) - An object containing paywall details. - **variationId** (string) - The active A/B variation ID. - **revision** (integer) - The paywall version number. - **hasViewConfiguration** (boolean) - Indicates if a Paywall Builder configuration exists. ### Request Example (Callback-based) ```swift import Adapty Adapty.getPaywall(placementId: "your_placement_id") { result in switch result { case let .success(paywall): print("Loaded paywall: \(paywall.variationId)") case let .failure(error): print("Failed to load paywall: \(error.localizedDescription)") } } ``` ### Request Example (async/await) ```swift do { let paywall = try await Adapty.getPaywall(placementId: "your_placement_id") print("Loaded paywall: \(paywall.variationId)") } catch { print("Failed: \(error)") } ``` ``` -------------------------------- ### AdaptyObserverModeDelegate Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Handle purchases in observer (non-Adapty) mode. Use this when your app manages its own purchase flow. AdaptyUI notifies you when a user initiates a purchase; call `onStartPurchase()` and `onFinishPurchase()` to drive the UI loading state. ```APIDOC ## `AdaptyObserverModeDelegate` — Handle purchases in observer (non-Adapty) mode Use observer mode when your app manages its own purchase flow (e.g., via a third-party payments SDK). AdaptyUI notifies you when a user initiates a purchase; call `onStartPurchase()` and `onFinishPurchase()` to drive the UI loading state. ```swift import Adapty import AdaptyUI import UIKit class PurchaseManager: NSObject, AdaptyObserverModeDelegate { func paywallController( _ controller: AdaptyPaywallController, didInitiatePurchase product: AdaptyPaywallProduct, onStartPurchase: @escaping () -> Void, onFinishPurchase: @escaping () -> Void ) { onStartPurchase() // Show loading state in the paywall UI MyCustomPurchaseService.buy(product.vendorProductId) { success in onFinishPurchase() // Hide loading state if success { controller.dismiss(animated: true) } } } } // Wire up observer mode delegate when creating the controller let controller = AdaptyUI.paywallController( for: paywall, products: nil, viewConfiguration: viewConfig, delegate: myDelegate, observerModeDelegate: PurchaseManager() ) present(controller, animated: true) ``` ``` -------------------------------- ### Fetch Paywall using Callback Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Load an AdaptyPaywall object using its placement ID via a callback. The placement ID is configured in the Adapty dashboard. ```swift import Adapty // Callback-based Adapty.getPaywall(placementId: "your_placement_id") { result in switch result { case let .success(paywall): // paywall.variationId – active A/B variation // paywall.revision – paywall version number // paywall.hasViewConfiguration – true if Paywall Builder config exists print("Loaded paywall: \(paywall.variationId)") case let .failure(error): print("Failed to load paywall: \(error.localizedDescription)") } } ``` -------------------------------- ### AdaptyUI.writeLog(level:message:) Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Emit SDK-namespaced log messages. Routes log messages through the Adapty SDK logging system, prefixed with the AdaptyUI version. Mirrors the log level set on `Adapty.logLevel`. ```APIDOC ## Logging ### `AdaptyUI.writeLog(level:message:)` — Emit SDK-namespaced log messages Routes log messages through the Adapty SDK logging system, prefixed with the AdaptyUI version. Mirrors the log level set on `Adapty.logLevel`. ```swift import Adapty import AdaptyUI // Set the log level on the parent Adapty SDK (controls AdaptyUI logs too) Adapty.logLevel = .verbose // .verbose | .info | .warn | .error | .none // Emit a custom log from your integration code AdaptyUI.writeLog( level: .info, message: "Paywall presented for placement: onboarding" ) // Console output: "[UI 2.11.3] Paywall presented for placement: onboarding" ``` ``` -------------------------------- ### AdaptyTagResolver Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Implement AdaptyTagResolver to replace custom tags defined in the Paywall Builder with dynamic runtime values. ```APIDOC ## Custom Tag Resolver ### `AdaptyTagResolver` — Substitute custom placeholders in paywall text Implement `AdaptyTagResolver` to replace custom tags defined in the Paywall Builder with dynamic runtime values. `Dictionary` conforms out of the box. ### Usage **Option 1: Use a dictionary literal directly (simplest)** ```swift let tagResolver: [String: String] = [ "USERNAME": "Alice", "PLAN_NAME": "Annual Pro", "TRIAL_DAYS": "7" ] ``` **Option 2: Custom resolver for dynamic values** ```swift class MyTagResolver: AdaptyTagResolver { func replacement(for tag: String) -> String? { switch tag { case "USERNAME": return UserSession.current?.name case "TRIAL_DAYS": return RemoteConfig.trialDays.map(String.init) default: return nil // Return nil to leave the tag as-is } } } ``` Pass either to `paywallController(for:...)` or `.paywall(...)`. ``` -------------------------------- ### Add Adapty SDK and AdaptyUI to Package.swift Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Declare Adapty SDK and AdaptyUI as dependencies in your Package.swift file for Swift Package Manager integration. ```swift // Package.swift dependencies: [ .package(url: "https://github.com/adaptyteam/AdaptySDK-iOS.git", "2.11.3" ..< "2.12.0"), // AdaptyUI is declared separately — add via Xcode or SPM package resolution ] ``` -------------------------------- ### Fetch Paywall with AdaptySDK Source: https://github.com/adaptyteam/adaptyui-ios/blob/main/README.md Use this method to retrieve a paywall object by its ID. Handle success by using the paywall or failure by managing the error. ```swift import Adapty Adapty.getPaywall("YOUR_PAYWALL_ID") { result in switch result { case let .success(paywall): // use loaded paywall case let .failure(error): // handle the error } } ``` -------------------------------- ### Present Visual Paywall Source: https://github.com/adaptyteam/adaptyui-ios/blob/main/README.md Once the paywall controller has been created, use this standard UIKit method to display the visual paywall on the device screen with animation. ```swift present(visualPaywall, animated: true) ``` -------------------------------- ### Emit SDK-Namespaced Log Messages with AdaptyUI.writeLog Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Route log messages through the Adapty SDK logging system, prefixed with the AdaptyUI version. The log level is controlled by `Adapty.logLevel`. Use this to emit custom logs from your integration code. ```swift import Adapty import AdaptyUI // Set the log level on the parent Adapty SDK (controls AdaptyUI logs too) Adapty.logLevel = .verbose // .verbose | .info | .warn | .error | .none // Emit a custom log from your integration code AdaptyUI.writeLog( level: .info, message: "Paywall presented for placement: onboarding" ) // Console output: "[UI 2.11.3] Paywall presented for placement: onboarding" ``` -------------------------------- ### Custom Tag Resolver for Paywalls Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Implement `AdaptyTagResolver` to dynamically replace placeholders in paywall text. A dictionary literal can be used for static values, or a custom class can provide dynamic replacements based on runtime conditions. ```swift import AdaptyUI // Option 1: Use a dictionary literal directly (simplest) let tagResolver: [String: String] = [ "USERNAME": "Alice", "PLAN_NAME": "Annual Pro", "TRIAL_DAYS": "7" ] // Option 2: Custom resolver for dynamic values class MyTagResolver: AdaptyTagResolver { func replacement(for tag: String) -> String? { switch tag { case "USERNAME": return UserSession.current?.name case "TRIAL_DAYS": return RemoteConfig.trialDays.map(String.init) default: return nil // Return nil to leave the tag as-is } } } // Pass either to paywallController(for:...) or .paywall(...) let controller = AdaptyUI.paywallController( for: paywall, products: nil, viewConfiguration: viewConfig, delegate: self, tagResolver: MyTagResolver() ) ``` -------------------------------- ### View.paywall(_:) Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt A SwiftUI view modifier that presents AdaptyPaywallController as a full-screen cover or sheet, using closure-based callbacks. ```APIDOC ## `View.paywall(isPresented:paywall:configuration:...)` — Present a paywall from SwiftUI A SwiftUI view modifier that presents `AdaptyPaywallController` as a full-screen cover (iOS 14+) or sheet, using closure-based callbacks instead of a delegate. ### Parameters - **isPresented** (Binding) - Controls the presentation of the paywall. - **paywall** (AdaptyPaywall) - The paywall object to display. - **products** (Optional<[AdaptyPaywallProduct]>) - Products to display. If nil, AdaptyUI fetches them automatically. - **configuration** (AdaptyUI.LocalizedViewConfiguration) - The localized view configuration for the paywall. - **tagResolver** (Optional<[String: String]>) - A dictionary for substituting custom tags in paywall text. - **fullScreen** (Bool) - If true, presents as a full-screen cover; otherwise, as a sheet. - **didPerformAction** (Optional<(AdaptyUI.Action) -> Void>) - Callback for when an action is performed in the paywall. - **didSelectProduct** (Optional<(AdaptyPaywallProduct) -> Void>) - Callback for when a product is selected. - **didStartPurchase** (Optional<(AdaptyPaywallProduct) -> Void>) - Callback for when a purchase attempt starts. - **didFinishPurchase** (Optional<(AdaptyPaywallProduct, AdaptyPurchaseInfo) -> Void>) - Callback for when a purchase is successfully completed. - **didFailPurchase** (Optional<(AdaptyPaywallProduct, Error) -> Void>) - Callback for when a purchase fails. - **didCancelPurchase** (Optional<(AdaptyPaywallProduct) -> Void>) - Callback for when a purchase is cancelled. - **didStartRestore** (Optional<() -> Void>) - Callback for when the restore process starts. - **didFinishRestore** (Optional<(AdaptyProfile) -> Void>) - Callback for when the restore process completes successfully. - **didFailRestore** (Optional<(Error) -> Void>) - Callback for when the restore process fails. - **didFailRendering** (Optional<(Error) -> Void>) - Callback for when the paywall fails to render. - **didFailLoadingProducts** (Optional<(Error) -> Bool>) - Callback for when products fail to load. Returning true attempts a retry. ``` -------------------------------- ### Add Adapty and AdaptyUI to Podfile Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Specify Adapty and AdaptyUI pods in your Podfile for CocoaPods integration. ```ruby # Podfile pod 'Adapty', '~> 2.11.3' pod 'AdaptyUI', '~> 2.11.3' ``` -------------------------------- ### Present Paywall from SwiftUI Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt Use the `.paywall` modifier in SwiftUI to present Adapty paywalls. It supports full-screen covers or sheets and handles various user interactions and purchase events via closures. Ensure paywall and view configuration are fetched before presenting. ```swift import Adapty import AdaptyUI import SwiftUI struct ContentView: View { @State private var paywallPresented = false @State private var paywall: AdaptyPaywall? @State private var viewConfig: AdaptyUI.LocalizedViewConfiguration? var body: some View { Button("Show Paywall") { paywallPresented = true } .task { do { let pw = try await Adapty.getPaywall(placementId: "onboarding") let vc = try await AdaptyUI.getViewConfiguration(forPaywall: pw) paywall = pw viewConfig = vc } catch { print("Setup error: \(error)") } } .paywall( isPresented: $paywallPresented, paywall: paywall!, products: nil, // Omit to let AdaptyUI fetch products automatically configuration: viewConfig!, tagResolver: ["USERNAME": "Alice"], fullScreen: true, // false → sheet instead of full-screen cover didPerformAction: { action in switch action { case .close: paywallPresented = false case let .openURL(url): UIApplication.shared.open(url, options: [:]) default: break } }, didSelectProduct: { product in print("Selected: \(product.vendorProductId)") }, didStartPurchase: { product in print("Started: \(product.vendorProductId)") }, didFinishPurchase: { product, info in print("Purchased: \(product.vendorProductId)") paywallPresented = false }, didFailPurchase: { product, error in print("Failed: \(error.localizedDescription)") }, didCancelPurchase: { product in print("Cancelled: \(product.vendorProductId)") }, didStartRestore: { print("Restore started") }, didFinishRestore: { profile in if profile.accessLevels["premium"]?.isActive == true { paywallPresented = false } }, didFailRestore: { error in print("Restore failed: \(error.localizedDescription)") }, didFailRendering: { error in print("Rendering error: \(error)") paywallPresented = false }, didFailLoadingProducts: { error in print("Products error: \(error)") return false // true = retry } ) } } ``` -------------------------------- ### AdaptyUIError Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt UI-specific error cases. `AdaptyUIError` covers rendering and configuration failures distinct from `AdaptyError` (network/purchase errors). Both error types appear in delegate callbacks. ```APIDOC ## Error Handling ### `AdaptyUIError` — UI-specific error cases `AdaptyUIError` covers rendering and configuration failures distinct from `AdaptyError` (network/purchase errors). Both error types appear in delegate callbacks. ```swift import AdaptyUI // AdaptyUIError cases: // .encoding(Error) – failed to encode/decode view configuration // .unsupportedTemplate(String) – paywall template not supported by this SDK version // .styleNotFound(String) – referenced style ID absent from configuration // .componentNotFound(String) – referenced component ID absent from configuration // .wrongComponentType(String) – component exists but is the wrong type // .rendering(Error) – error thrown during UI rendering func paywallController(_ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError) { // AdaptyError wraps AdaptyUIError for rendering failures print("Rendering failed: \(error.localizedDescription)") // Inspect underlying UI error if needed if let uiError = error.originalError as? AdaptyUIError { switch uiError { case let .unsupportedTemplate(desc): print("Update AdaptyUI SDK – unsupported template: \(desc)") case let .rendering(underlying): print("Render engine error: \(underlying)") default: print("UI error: \(uiError.debugDescription)") } } controller.dismiss(animated: true) } ``` ``` -------------------------------- ### Handle AdaptyUIError in Delegate Callbacks Source: https://context7.com/adaptyteam/adaptyui-ios/llms.txt AdaptyUIError covers rendering and configuration failures. AdaptyError wraps AdaptyUIError for rendering failures. Inspect the `originalError` to differentiate between Adapty and AdaptyUI specific issues. ```swift import AdaptyUI // AdaptyUIError cases: // .encoding(Error) – failed to encode/decode view configuration // .unsupportedTemplate(String) – paywall template not supported by this SDK version // .styleNotFound(String) – referenced style ID absent from configuration // .componentNotFound(String) – referenced component ID absent from configuration // .wrongComponentType(String) – component exists but is the wrong type // .rendering(Error) – error thrown during UI rendering func paywallController(_ controller: AdaptyPaywallController, didFailRenderingWith error: AdaptyError) { // AdaptyError wraps AdaptyUIError for rendering failures print("Rendering failed: \(error.localizedDescription)") // Inspect underlying UI error if needed if let uiError = error.originalError as? AdaptyUIError { switch uiError { case let .unsupportedTemplate(desc): print("Update AdaptyUI SDK – unsupported template: \(desc)") case let .rendering(underlying): print("Render engine error: \(underlying)") default: print("UI error: \(uiError.debugDescription)") } } controller.dismiss(animated: true) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.