---
title: "Display flows & paywalls - iOS"
description: "Present flows and paywalls to users in your iOS app."
---

<MethodPromo method="getFlow" label="Display flows and paywalls" />

If you've created a flow or paywall, you don't need to worry about rendering it in your mobile app code to display it to the user. Such a flow or paywall contains both what should be shown within it and how it should be shown.

To get the `AdaptyUI.FlowConfiguration` object used below, see [Get flows and paywalls](get-pb-paywalls).

## Present flows and paywalls in SwiftUI

### Present as a modal view

In order to display a flow or paywall on the device screen as a modal view, use the `.flow` modifier in SwiftUI. The minimal call requires `isPresented`, `flowConfiguration`, and the five required callbacks:

```swift showLineNumbers title="SwiftUI"
.flow(
    isPresented: $flowPresented,
    flowConfiguration: <AdaptyUI.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 }
)
```

For more control, add optional callbacks like `didPerformAction` to handle button taps:

```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: <AdaptyUI.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 }
      )
}
```

Parameters:

| Parameter              | Required | Description                                                                                                                                                                                                                                                                |
|:-----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **isPresented**        | required | A binding that manages whether the flow or paywall screen is displayed.                                                                                                                                                                                                    |
| **flowConfiguration**  | required | An `AdaptyUI.FlowConfiguration` object containing visual details of the flow or paywall. Use the `AdaptyUI.getFlowConfiguration(forFlow:)` method. Refer to [Get flows and paywalls](get-pb-paywalls) for more details.                                                    |
| **didFinishPurchase**  | required | Invoked when `Adapty.makePurchase()` completes successfully. The flow does not dismiss automatically — set your presentation binding to `false` here, or do nothing to let the flow continue past the purchase.                                                            |
| **didFailPurchase**    | required | Invoked when `Adapty.makePurchase()` fails.                                                                                                                                                                                                                                |
| **didFinishRestore**   | required | Invoked when `Adapty.restorePurchases()` completes successfully.                                                                                                                                                                                                           |
| **didFailRestore**     | required | Invoked when `Adapty.restorePurchases()` fails.                                                                                                                                                                                                                            |
| **didReceiveError**    | required | Invoked for a rendering error or a runtime error from the flow script (for example, a JavaScript exception, `AdaptyUIError` code `4105`). For rendering errors, [contact Adapty Support](mailto:support@adapty.io).                                                       |
| **fullScreen**         | optional | Determines if the flow or paywall appears in full-screen mode or as a sheet. Defaults to `true`.                                                                                                                                                                           |
| **didAppear**          | optional | Invoked when the flow or paywall view was presented.                                                                                                                                                                                                                       |
| **didDisappear**       | optional | Invoked when the flow or paywall view was dismissed.                                                                                                                                                                                                                       |
| **didPerformAction**   | optional | Invoked when a user clicks a button. Two action IDs are pre-defined: `close` and `openURL`; others are custom and can be set in the builder.                                                                                                                               |
| **didSelectProduct**   | optional | Invoked when a product is selected for purchase by the user or by the system.                                                                                                                                                                                              |
| **didStartPurchase**   | optional | Invoked when the user begins the purchase process.                                                                                                                                                                                                                         |
| **didFinishWebPaymentNavigation** | optional | Invoked when web payment navigation finishes.                                                                                                                                                                                                                    |
| **didStartRestore**    | optional | Invoked when the user starts the restore process.                                                                                                                                                                                                                          |
| **didFailLoadingProducts** | optional | Invoked when errors occur during product loading. Return `true` to retry loading.                                                                                                                                                                                      |
| **didPartiallyLoadProducts** | optional | Invoked when products are partially loaded.                                                                                                                                                                                                                          |
| **showAlertItem**      | optional | A binding that manages the display of alert items above the flow or paywall.                                                                                                                                                                                               |
| **showAlertBuilder**   | optional | A function for rendering the alert view.                                                                                                                                                                                                                                   |
| **placeholderBuilder** | optional | A function for rendering the placeholder view while the flow or paywall is loading. Defaults to a `ProgressView`.                                                                                                                                                          |

Refer to the [iOS - Handling events](ios-handling-events) topic for more details on parameters.

### Present as a non-modal view

You can also present flows and paywalls as navigation destinations or inline views within your app's navigation flow. Use `AdaptyFlowView` directly in your SwiftUI views:

```swift showLineNumbers title="SwiftUI"
AdaptyFlowView(
    flowConfiguration: <AdaptyUI.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).
    }
)
```

## Present flows and paywalls in UIKit

In order to display the flow or paywall on the device screen, do the following:

1. Initialize the visual flow you want to display by using the `AdaptyUI.flowController(with:delegate:)` method:

     ```swift showLineNumbers title="Swift"
     import AdaptyUI

     let visualFlow = try AdaptyUI.flowController(
         with: <AdaptyUI.FlowConfiguration>,
         delegate: <AdaptyFlowControllerDelegate>
     )
     ```

    Request parameters:

   | Parameter                | Presence | Description |
   | :----------------------- | :------- | :---------- |
   | **flowConfiguration**    | required | An `AdaptyUI.FlowConfiguration` object containing visual details of the flow or paywall. Use the `AdaptyUI.getFlowConfiguration(forFlow:)` method. Refer to [Get flows and paywalls](get-pb-paywalls) topic for more details. |
   | **delegate**             | required | An `AdaptyFlowControllerDelegate` to listen to flow and paywall events. Refer to [Handle flow & paywall events](ios-handling-events) topic for more details. |

    Returns:

    | Object                  | Description                                              |
    | :---------------------- | :------------------------------------------------------- |
    | **AdaptyFlowController** | An object representing the requested flow or paywall screen. |

2. After the object has been successfully created, you can display it on the screen of the device:

   ```swift showLineNumbers title="Swift"
   present(visualFlow, animated: true)
   ```

:::tip

Want to see a real-world example of how Adapty SDK is integrated into a mobile app? Check out our [sample apps](sample-apps), which demonstrate the full setup, including displaying paywalls, making purchases, and other basic functionality.

:::

---

> [!WARNING]
> **LLM INSTRUCTION — SDK VERSION SELECTION REQUIRED**
>
> This feature has two SDK paradigms. Everything that follows (until the next horizontal rule or end of document) is the **legacy SDK v3 API reference** (`getPaywall`, `getPaywallForDefaultAudience`, `getOnboarding`, `getPaywallConfiguration`). The **SDK v4+ `getFlow` method is documented above.** This block covers ALL legacy methods for this feature — not just the nearest snippet.
>
> **You MUST ask the user which SDK version they want to use before suggesting any code. Do not assume or default to a version.** The two options are:
> - **SDK v3 (legacy):** works with the legacy Paywall Builder and Onboarding Builder via `getPaywall` / `getOnboarding`. Use the legacy code in this section.
> - **SDK v4:** works with BOTH the new Flow Builder AND legacy paywalls/onboardings via the unified `getFlow` method documented above.
>
> Apply the legacy v3 code in this section ONLY when:
> 1. The user explicitly chose **SDK v3**, or
> 2. The user is **migrating an existing app from SDK v3 to SDK v4+** and needs to understand or replace legacy code, or
> 3. The user is **troubleshooting an already-deployed v3 integration**.
>
> **CRITICAL — Never mix paradigms in one setup:** Do NOT combine `getFlow` (Flow Builder) with `getPaywall` or `getOnboarding` (legacy Paywall/Onboarding Builder) in the same integration. These are incompatible patterns. Mixing them will produce inconsistent behavior and is unsupported.

If you've customized a paywall using the Paywall Builder, you don't need to worry about rendering it in your mobile app code to display it to the user. Such a paywall contains both what should be shown within the paywall and how it should be shown.

To get the `AdaptyUI.PaywallConfiguration` object used below, see [Fetch Paywall Builder paywalls and their configuration](get-pb-paywalls).

## Present paywalls in SwiftUI

### Present as a modal view

In order to display the visual paywall on the device screen as a modal view, use the `.paywall` modifier in 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: <AdaptyUI.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 }
      )
}
```

Parameters:

| Parameter                         | Required | Description                                                                                                                                                                                                                                                                                                                    |
|:----------------------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **isPresented**                   | required | A binding that manages whether the paywall screen is displayed.                                                                                                                                                                                                                                                                |
| **paywallConfiguration**          | required | An `AdaptyUI.PaywallConfiguration` object containing visual details of the paywall. Use the `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)` method. Refer to [Fetch Paywall Builder paywalls and their configuration](get-pb-paywalls) topic for more details. |
| **didFailPurchase**               | required | Invoked when `Adapty.makePurchase()` fails.                                                                                                                                                                                                                                                                                    |
| **didFinishRestore**              | required | Invoked when `Adapty.restorePurchases()` completes successfully.                                                                                                                                                                                                                                                               |
| **didFailRestore**                | required | Invoked when `Adapty.restorePurchases()` fails.                                                                                                                                                                                                                                                                               |
| **didFailRendering**              | required | Invoked if an error occurs while rendering the interface. In this case, [contact Adapty Support](mailto:support@adapty.io).                                                                                                                                                                                                    |
| **fullScreen**                    | optional | Determines if the paywall appears in full-screen mode or as a modal. Defaults to `true`.                                                                                                                                                                                                                                       |
| **didAppear**                     | optional | Invoked when the paywall view was presented.                                                                                                                                                                                                                                                                                   |
| **didDisappear**                  | optional | Invoked when the paywall view was dismissed.                                                                                                                                                                                                                                                                                   |
| **didPerformAction**              | optional | Invoked when a user clicks a button. Different buttons have different action IDs. Two action IDs are pre-defined: `close` and `openURL`, while others are custom and can be set in the builder.                                                                                                                                |
| **didSelectProduct**              | optional | If the product was selected for purchase (by a user or by the system), this callback will be invoked.                                                                                                                                                                                                                          |
| **didStartPurchase**              | optional | Invoked when the user begins the purchase process.                                                                                                                                                                                                                                                                             |
| **didFinishPurchase**             | optional | Invoked when `Adapty.makePurchase()` completes successfully.                                                                                                                                                                                                                                                                   |
| **didFinishWebPaymentNavigation** | optional | Invoked when web payment navigation finishes.                                                                                                                                                                                                                                                                                  |
| **didStartRestore**               | optional | Invoked when the user starts the restore process.                                                                                                                                                                                                                                                                              |
| **didFailLoadingProducts**        | optional | Invoked when errors occur during product loading. Return `true` to retry loading.                                                                                                                                                                                                                                              |
| **didPartiallyLoadProducts**      | optional | Invoked when products are partially loaded.                                                                                                                                                                                                                                                                                    |
| **showAlertItem**                 | optional | A binding that manages the display of alert items above the paywall.                                                                                                                                                                                                                                                           |
| **showAlertBuilder**              | optional | A function for rendering the alert view.                                                                                                                                                                                                                                                                                       |
| **placeholderBuilder**            | optional | A function for rendering the placeholder view while the paywall is loading.                                                                                                                                                                                                                                                    |

Refer to the [iOS - Handling events](ios-handling-events) topic for more details on parameters.

### Present as a non-modal view

You can also present paywalls as navigation destinations or inline views within your app's navigation flow. Use `AdaptyPaywallView` directly in your SwiftUI views:

```swift showLineNumbers title="SwiftUI"
AdaptyPaywallView(
    paywallConfiguration: <AdaptyUI.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
    }
)
```

## Present paywalls in UIKit

In order to display the visual paywall on the device screen, do the following:

1. Initialize the visual paywall you want to display by using the  `.paywallController(for:products:viewConfiguration:delegate:)` method:

     ```swift showLineNumbers title="Swift"
     import AdaptyUI

     let visualPaywall = AdaptyUI.paywallController(
         with: <paywall configuration object>,
         delegate: <AdaptyPaywallControllerDelegate>
     )
     ```

    Request parameters:

   | Parameter                | Presence | Description |
   | :----------------------- | :------- | :---------- |
   | **paywall configuration**         | required | An `AdaptyUI.PaywallConfiguration` object containing visual details of the paywall. Use the `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)` method.  Refer to [Fetch Paywall Builder paywalls and their configuration](get-pb-paywalls) topic for more details. |
   | **delegate**            | required | An `AdaptyPaywallControllerDelegate` to listen to paywall events. Refer to [Handling paywall events](ios-handling-events) topic for more details.

    Returns:

    | Object                  | Description                                          |
    | :---------------------- | :--------------------------------------------------- |
    | **AdaptyPaywallController** | An object, representing the requested paywall screen |

2. After the object has been successfully created, you can display it on the screen of the device:

   ```swift showLineNumbers title="Swift"
   present(visualPaywall, animated: true)
   ```

:::tip

Want to see a real-world example of how Adapty SDK is integrated into a mobile app? Check out our [sample apps](sample-apps), which demonstrate the full setup, including displaying paywalls, making purchases, and other basic functionality.

:::

---