---
title: "Hiển thị flows & paywalls - iOS"
description: "Hiển thị flows và paywalls cho người dùng trong ứng dụng iOS của bạn."
---

<MethodPromo method="getFlow" label="Hiển thị flows và paywalls" />

Nếu bạn đã tạo một flow hoặc paywall, bạn không cần lo lắng về việc render nó trong code ứng dụng để hiển thị cho người dùng. Flow hoặc paywall đó đã chứa cả nội dung cần hiển thị lẫn cách hiển thị.

Để lấy đối tượng `AdaptyUI.FlowConfiguration` dùng bên dưới, xem [Lấy flows và paywalls](get-pb-paywalls).

## Hiển thị flows và paywalls trong SwiftUI \{#present-flows-and-paywalls-in-swiftui\}

### Hiển thị dạng modal view \{#present-as-a-modal-view\}

Để hiển thị flow hoặc paywall trên màn hình thiết bị dưới dạng modal view, dùng modifier `.flow` trong SwiftUI. Cách gọi tối thiểu cần `isPresented`, `flowConfiguration`, và bốn callback bắt buộc:

```swift showLineNumbers title="SwiftUI"
.flow(
    isPresented: $flowPresented,
    flowConfiguration: <AdaptyUI.FlowConfiguration>,
    didFailPurchase: { _, _ in /* handle the error */ },
    didFinishRestore: { _ in /* check access level and dismiss */ },
    didFailRestore: { _ in /* handle the error */ },
    didReceiveError: { _ in flowPresented = false }
)
```

Để kiểm soát nhiều hơn, thêm các callback tùy chọn như `didPerformAction` để xử lý sự kiện nhấn nút và `didFinishPurchase` để phản hồi khi mua hàng thành công:

```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
              }
          },
          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 }
      )
}
```

Các tham số:

| Tham số | Bắt buộc | Mô tả |
|:-----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **isPresented** | bắt buộc | Một binding quản lý việc hiển thị màn hình flow hoặc paywall. |
| **flowConfiguration** | bắt buộc | Đối tượng `AdaptyUI.FlowConfiguration` chứa thông tin hiển thị của flow hoặc paywall. Dùng phương thức `AdaptyUI.getFlowConfiguration(forFlow:)`. Xem [Lấy flows và paywalls](get-pb-paywalls) để biết thêm chi tiết. |
| **didFailPurchase** | bắt buộc | Được gọi khi `Adapty.makePurchase()` thất bại. |
| **didFinishRestore** | bắt buộc | Được gọi khi `Adapty.restorePurchases()` hoàn thành thành công. |
| **didFailRestore** | bắt buộc | Được gọi khi `Adapty.restorePurchases()` thất bại. |
| **didReceiveError** | bắt buộc | Được gọi khi có lỗi render hoặc lỗi runtime từ flow script (ví dụ: exception JavaScript, mã `AdaptyUIError` `4105`). Với lỗi render, hãy [liên hệ Adapty Support](mailto:support@adapty.io). |
| **fullScreen** | tùy chọn | Xác định flow hoặc paywall hiển thị toàn màn hình hay dạng sheet. Mặc định là `true`. |
| **didAppear** | tùy chọn | Được gọi khi view của flow hoặc paywall được hiển thị. |
| **didDisappear** | tùy chọn | Được gọi khi view của flow hoặc paywall bị đóng. |
| **didPerformAction** | tùy chọn | Được gọi khi người dùng nhấn một nút. Có hai action ID được định nghĩa sẵn: `close` và `openURL`; các ID còn lại là tùy chỉnh và có thể được thiết lập trong builder. |
| **didSelectProduct** | tùy chọn | Được gọi khi người dùng hoặc hệ thống chọn một sản phẩm để mua. |
| **didStartPurchase** | tùy chọn | Được gọi khi người dùng bắt đầu quá trình mua hàng. |
| **didFinishPurchase** | tùy chọn | Được gọi khi `Adapty.makePurchase()` hoàn thành thành công. |
| **didFinishWebPaymentNavigation** | tùy chọn | Được gọi khi điều hướng thanh toán web kết thúc. |
| **didStartRestore** | tùy chọn | Được gọi khi người dùng bắt đầu quá trình khôi phục. |
| **didFailLoadingProducts** | tùy chọn | Được gọi khi có lỗi trong quá trình tải sản phẩm. Trả về `true` để thử tải lại. |
| **didPartiallyLoadProducts** | tùy chọn | Được gọi khi sản phẩm chỉ được tải một phần. |
| **showAlertItem** | tùy chọn | Một binding quản lý việc hiển thị các alert item phía trên flow hoặc paywall. |
| **showAlertBuilder** | tùy chọn | Hàm để render alert view. |
| **placeholderBuilder** | tùy chọn | Hàm để render view placeholder trong khi flow hoặc paywall đang tải. Mặc định là `ProgressView`. |

Xem chủ đề [iOS - Xử lý sự kiện](ios-handling-events) để biết thêm chi tiết về các tham số.

### Hiển thị dạng non-modal view \{#present-as-a-non-modal-view\}

Bạn cũng có thể hiển thị flows và paywalls dưới dạng navigation destination hoặc inline view trong navigation flow của ứng dụng. Dùng `AdaptyFlowView` trực tiếp trong các SwiftUI view của bạn:

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

## Hiển thị flows và paywalls trong UIKit \{#present-flows-and-paywalls-in-uikit\}

Để hiển thị flow hoặc paywall trên màn hình thiết bị, thực hiện các bước sau:

1. Khởi tạo visual flow bạn muốn hiển thị bằng phương thức `AdaptyUI.flowController(with:delegate:)`:

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

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

    Tham số yêu cầu:

   | Tham số | Bắt buộc | Mô tả |
   | :----------------------- | :------- | :---------- |
   | **flowConfiguration** | bắt buộc | Đối tượng `AdaptyUI.FlowConfiguration` chứa thông tin hiển thị của flow hoặc paywall. Dùng phương thức `AdaptyUI.getFlowConfiguration(forFlow:)`. Xem chủ đề [Lấy flows và paywalls](get-pb-paywalls) để biết thêm chi tiết. |
   | **delegate** | bắt buộc | Một `AdaptyFlowControllerDelegate` để lắng nghe các sự kiện của flow và paywall. Xem chủ đề [Xử lý sự kiện flow & paywall](ios-handling-events) để biết thêm chi tiết. |

    Giá trị trả về:

    | Đối tượng | Mô tả |
    | :---------------------- | :------------------------------------------------------- |
    | **AdaptyFlowController** | Đối tượng đại diện cho màn hình flow hoặc paywall được yêu cầu. |

2. Sau khi đối tượng được tạo thành công, bạn có thể hiển thị nó trên màn hình thiết bị:

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

:::tip

Bạn muốn xem ví dụ thực tế về cách tích hợp Adapty SDK vào ứng dụng di động? Hãy xem [ứng dụng mẫu](sample-apps) của chúng tôi, nơi minh họa toàn bộ quá trình thiết lập, bao gồm hiển thị paywalls, thực hiện mua hàng và các chức năng cơ bản khác.

:::

---

> [!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 (stable):** works with the legacy Paywall Builder and Onboarding Builder via `getPaywall` / `getOnboarding`. Use the legacy code in this section.
> - **SDK v4 (beta):** works with BOTH the new Flow Builder AND legacy paywalls/onboardings via the unified `getFlow` method documented above.
>
> **If the user chooses the v4 beta, warn them that Adapty iOS SDK 4.0 is a pre-release.** Swift Package Manager does not resolve beta versions through the **Up to Next Major Version** (`from:`) rule, so they must pin the exact version:
> - In Xcode, set the **Dependency Rule** to **Exact Version** and enter `4.0.0-beta.1`.
> - In `Package.swift`, use `.exact("4.0.0-beta.1")`.
>
> 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.

Nếu bạn đã tùy chỉnh paywall bằng Paywall Builder, bạn không cần lo lắng về việc render nó trong code ứng dụng để hiển thị cho người dùng. Paywall đó đã chứa cả nội dung cần hiển thị lẫn cách hiển thị.

Để lấy đối tượng `AdaptyUI.PaywallConfiguration` dùng bên dưới, xem [Lấy paywalls Paywall Builder và cấu hình của chúng](get-pb-paywalls).

## Hiển thị paywalls trong SwiftUI \{#present-paywalls-in-swiftui\}

### Hiển thị dạng modal view \{#present-as-a-modal-view\}

Để hiển thị visual paywall trên màn hình thiết bị dưới dạng modal view, dùng modifier `.paywall` trong 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 }
      )
}
```

Các tham số:

| Tham số | Bắt buộc | Mô tả |
|:----------------------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **isPresented** | bắt buộc | Một binding quản lý việc hiển thị màn hình paywall. |
| **paywallConfiguration** | bắt buộc | Đối tượng `AdaptyUI.PaywallConfiguration` chứa thông tin hiển thị của paywall. Dùng phương thức `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Xem chủ đề [Lấy paywalls Paywall Builder và cấu hình của chúng](get-pb-paywalls) để biết thêm chi tiết. |
| **didFailPurchase** | bắt buộc | Được gọi khi `Adapty.makePurchase()` thất bại. |
| **didFinishRestore** | bắt buộc | Được gọi khi `Adapty.restorePurchases()` hoàn thành thành công. |
| **didFailRestore** | bắt buộc | Được gọi khi `Adapty.restorePurchases()` thất bại. |
| **didFailRendering** | bắt buộc | Được gọi nếu có lỗi xảy ra khi render giao diện. Trong trường hợp này, hãy [liên hệ Adapty Support](mailto:support@adapty.io). |
| **fullScreen** | tùy chọn | Xác định paywall hiển thị toàn màn hình hay dạng modal. Mặc định là `true`. |
| **didAppear** | tùy chọn | Được gọi khi view của paywall được hiển thị. |
| **didDisappear** | tùy chọn | Được gọi khi view của paywall bị đóng. |
| **didPerformAction** | tùy chọn | Được gọi khi người dùng nhấn một nút. Các nút khác nhau có action ID khác nhau. Có hai action ID được định nghĩa sẵn: `close` và `openURL`; các ID còn lại là tùy chỉnh và có thể được thiết lập trong builder. |
| **didSelectProduct** | tùy chọn | Được gọi khi người dùng hoặc hệ thống chọn một sản phẩm để mua. |
| **didStartPurchase** | tùy chọn | Được gọi khi người dùng bắt đầu quá trình mua hàng. |
| **didFinishPurchase** | tùy chọn | Được gọi khi `Adapty.makePurchase()` hoàn thành thành công. |
| **didFinishWebPaymentNavigation** | tùy chọn | Được gọi khi điều hướng thanh toán web kết thúc. |
| **didStartRestore** | tùy chọn | Được gọi khi người dùng bắt đầu quá trình khôi phục. |
| **didFailLoadingProducts** | tùy chọn | Được gọi khi có lỗi trong quá trình tải sản phẩm. Trả về `true` để thử tải lại. |
| **didPartiallyLoadProducts** | tùy chọn | Được gọi khi sản phẩm chỉ được tải một phần. |
| **showAlertItem** | tùy chọn | Một binding quản lý việc hiển thị các alert item phía trên paywall. |
| **showAlertBuilder** | tùy chọn | Hàm để render alert view. |
| **placeholderBuilder** | tùy chọn | Hàm để render view placeholder trong khi paywall đang tải. |

Xem chủ đề [iOS - Xử lý sự kiện](ios-handling-events) để biết thêm chi tiết về các tham số.

### Hiển thị dạng non-modal view \{#present-as-a-non-modal-view\}

Bạn cũng có thể hiển thị paywalls dưới dạng navigation destination hoặc inline view trong navigation flow của ứng dụng. Dùng `AdaptyPaywallView` trực tiếp trong các SwiftUI view của bạn:

```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
    }
)
```

## Hiển thị paywalls trong UIKit \{#present-paywalls-in-uikit\}

Để hiển thị visual paywall trên màn hình thiết bị, thực hiện các bước sau:

1. Khởi tạo visual paywall bạn muốn hiển thị bằng phương thức `.paywallController(for:products:viewConfiguration:delegate:)`:

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

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

    Tham số yêu cầu:

   | Tham số | Bắt buộc | Mô tả |
   | :----------------------- | :------- | :---------- |
   | **paywall configuration** | bắt buộc | Đối tượng `AdaptyUI.PaywallConfiguration` chứa thông tin hiển thị của paywall. Dùng phương thức `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Xem chủ đề [Lấy paywalls Paywall Builder và cấu hình của chúng](get-pb-paywalls) để biết thêm chi tiết. |
   | **delegate** | bắt buộc | Một `AdaptyPaywallControllerDelegate` để lắng nghe các sự kiện paywall. Xem chủ đề [Xử lý sự kiện paywall](ios-handling-events) để biết thêm chi tiết. |

    Giá trị trả về:

    | Đối tượng | Mô tả |
    | :---------------------- | :--------------------------------------------------- |
    | **AdaptyPaywallController** | Đối tượng đại diện cho màn hình paywall được yêu cầu |

2. Sau khi đối tượng được tạo thành công, bạn có thể hiển thị nó trên màn hình thiết bị:

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

:::tip

Bạn muốn xem ví dụ thực tế về cách tích hợp Adapty SDK vào ứng dụng di động? Hãy xem [ứng dụng mẫu](sample-apps) của chúng tôi, nơi minh họa toàn bộ quá trình thiết lập, bao gồm hiển thị paywalls, thực hiện mua hàng và các chức năng cơ bản khác.

:::

---