---
title: "Xử lý sự kiện onboarding trong iOS SDK"
description: "Xử lý các sự kiện liên quan đến onboarding trong iOS bằng Adapty."
---

:::tip
**Từ SDK v4 (beta)**, bạn có thể xây dựng [flow](get-pb-paywalls) như một lựa chọn mạnh mẽ hơn so với onboarding. Không giống như onboarding chạy bên trong WebView, flow render trực tiếp trên thiết bị — mang lại animation mượt mà hơn, giao diện iOS nhất quán, tải nhanh hơn và không phụ thuộc vào WebView runtime. Xem [Lấy flow & paywall](get-pb-paywalls) và [Hiển thị flow & paywall](ios-present-paywalls) để bắt đầu.
:::

Trước khi bắt đầu, hãy đảm bảo rằng:

1. Bạn đã cài đặt [Adapty iOS SDK](sdk-installation-ios) phiên bản 3.8.0 trở lên.
2. Bạn đã [tạo một onboarding](create-onboarding).
3. Bạn đã thêm onboarding vào một [placement](placements).

Các onboarding được cấu hình bằng builder sẽ tạo ra các sự kiện mà ứng dụng của bạn có thể xử lý. Hãy tìm hiểu cách xử lý các sự kiện này bên dưới.

Để kiểm soát hoặc theo dõi các tiến trình xảy ra trên màn hình onboarding trong ứng dụng di động của bạn, hãy triển khai các phương thức `AdaptyOnboardingControllerDelegate`.

## Hành động tùy chỉnh \{#custom-actions\}

Trong builder, bạn có thể thêm hành động **custom** cho một nút và gán cho nó một ID.

  <img src="/assets/shared/img/ios-events-1.webp"
  style={{
    border: '1px solid #727272', /* border width and color */
    width: '700px', /* image width */
    display: 'block', /* for alignment */
    margin: '0 auto' /* center alignment */
  }}
/>

Sau đó, bạn có thể sử dụng ID này trong code và xử lý nó như một hành động tùy chỉnh. Ví dụ: khi người dùng nhấn vào một nút tùy chỉnh như **Login** hoặc **Allow notifications**, phương thức delegate `onboardingController` sẽ được kích hoạt với case `.custom(id:)` và tham số `actionId` chính là **Action ID** từ builder. Bạn có thể tự tạo ID của riêng mình, chẳng hạn như "allowNotifications".

```swift showLineNumbers    
func onboardingController(_ controller: AdaptyOnboardingController, onCustomAction action: AdaptyOnboardingsCustomAction) {
    if action.actionId == "allowNotifications" {
        // Request notification permissions
    }
}
    
func onboardingController(_ controller: AdaptyOnboardingController, didFailWithError error: AdaptyUIError) {
    // Handle errors
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
  "actionId": "allowNotifications",
  "meta": {
    "onboardingId": "onboarding_123",
    "screenClientId": "profile_screen",
    "screenIndex": 0,
    "screensTotal": 3
  }
}
```
</Details>

## Đóng onboarding \{#closing-onboarding\}

Onboarding được coi là đã đóng khi người dùng nhấn vào một nút có hành động **Close** được gán.

  <img src="/assets/shared/img/ios-events-2.webp"
  style={{
    border: '1px solid #727272', /* border width and color */
    width: '700px', /* image width */
    display: 'block', /* for alignment */
    margin: '0 auto' /* center alignment */
  }}
/>

:::important
Lưu ý rằng bạn cần tự xử lý những gì xảy ra khi người dùng đóng onboarding. Ví dụ: bạn cần dừng hiển thị onboarding đó.
:::
Ví dụ:

```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onCloseAction action: AdaptyOnboardingsCloseAction) {
    controller.dismiss(animated: true)
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
  "action_id": "close_button",
  "meta": {
    "onboarding_id": "onboarding_123",
    "screen_cid": "final_screen",
    "screen_index": 3,
    "total_screens": 4
  }
}
```

</Details>

## Mở paywall \{#opening-a-paywall\}

:::tip
Xử lý sự kiện này để mở paywall nếu bạn muốn mở nó bên trong onboarding. Nếu bạn muốn mở paywall sau khi đóng onboarding, có một cách đơn giản hơn — xử lý [`AdaptyOnboardingsCloseAction`](#closing-onboarding) và mở paywall mà không cần dựa vào dữ liệu sự kiện.
:::

Cách liền mạch nhất để làm việc với paywall trong onboarding là đặt action ID bằng với placement ID của paywall. Như vậy, sau sự kiện `AdaptyOnboardingsOpenPaywallAction`, bạn có thể dùng placement ID để lấy và mở paywall ngay lập tức.

Lưu ý rằng tại một thời điểm chỉ có thể hiển thị một view (paywall hoặc onboarding) trên màn hình. Nếu bạn hiển thị paywall chồng lên onboarding, bạn không thể lập trình điều khiển onboarding ở nền. Việc cố gắng dismiss onboarding sẽ đóng paywall thay vào đó, khiến onboarding vẫn hiển thị. Để tránh điều này, hãy luôn dismiss view onboarding trước khi hiển thị paywall.

```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onPaywallAction action: AdaptyOnboardingsOpenPaywallAction) {
    // Dismiss onboarding before presenting the flow
    controller.dismiss(animated: true) {
        Task {
            do {
                // Get the flow using the placement ID from the action
                let flow = try await Adapty.getFlow(placementId: action.actionId)

                // Get the flow configuration
                let flowConfiguration = try await AdaptyUI.getFlowConfiguration(
                    forFlow: flow
                )

                // Create and present the flow controller
                let flowController = try AdaptyUI.flowController(
                    with: flowConfiguration,
                    delegate: self
                )

                // Present the flow from the root view controller
                if let rootVC = UIApplication.shared.windows.first?.rootViewController {
                    rootVC.present(flowController, animated: true)
                }
            } catch {
                // Handle any errors that occur during flow loading
                print("Failed to present flow: \(error)")
            }
        }
    }
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
    "action_id": "premium_offer_1",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "pricing_screen",
        "screen_index": 2,
        "total_screens": 4
    }
}
```

</Details>

## Hoàn tất tải onboarding \{#finishing-loading-onboarding\}

Khi onboarding hoàn tất tải, phương thức này sẽ được gọi:

```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, didFinishLoading action: OnboardingsDidFinishLoadingAction) {
    // Handle loading completion
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "welcome_screen",
        "screen_index": 0,
        "total_screens": 4
    }
}
```

</Details>

## Theo dõi điều hướng \{#tracking-navigation\}

Phương thức `onAnalyticsEvent` được gọi khi các sự kiện analytics khác nhau xảy ra trong quá trình onboarding.

Đối tượng `event` có thể là một trong các loại sau:
|Loại | Mô tả |
|------------|-------------|
| `onboardingStarted` | Khi onboarding đã được tải xong |
| `screenPresented` | Khi bất kỳ màn hình nào được hiển thị |
| `screenCompleted` | Khi một màn hình hoàn tất. Bao gồm `elementId` tùy chọn (định danh của phần tử đã hoàn thành) và `reply` tùy chọn (phản hồi từ người dùng). Được kích hoạt khi người dùng thực hiện bất kỳ hành động nào để thoát khỏi màn hình. |
| `secondScreenPresented` | Khi màn hình thứ hai được hiển thị |
| `userEmailCollected` | Được kích hoạt khi email của người dùng được thu thập qua trường nhập liệu |
| `onboardingCompleted` | Được kích hoạt khi người dùng đến màn hình có ID `final`. Nếu bạn cần sự kiện này, hãy [gán ID `final` cho màn hình cuối cùng](design-onboarding). |
| `unknown` | Cho bất kỳ loại sự kiện không nhận dạng được. Bao gồm `name` (tên của sự kiện không xác định) và `meta` (metadata bổ sung) |

Mỗi sự kiện bao gồm thông tin `meta` chứa:
| Trường | Mô tả |
|------------|-------------|
| `onboardingId` | Định danh duy nhất của onboarding flow |
| `screenClientId` | Định danh của màn hình hiện tại |
| `screenIndex` | Vị trí của màn hình hiện tại trong flow |
| `screensTotal` | Tổng số màn hình trong flow |

Đây là ví dụ về cách bạn có thể sử dụng các sự kiện analytics để theo dõi:

```swift
func onboardingController(_ controller: AdaptyOnboardingController, onAnalyticsEvent event: AdaptyOnboardingsAnalyticsEvent) {
    switch event {
    case .onboardingStarted(let meta):
        // Track onboarding start
        trackEvent("onboarding_started", meta: meta)
    case .screenPresented(let meta):
        // Track screen presentation
        trackEvent("screen_presented", meta: meta)
    case .screenCompleted(let meta, let elementId, let reply):
        // Track screen completion with user response
        trackEvent("screen_completed", meta: meta, elementId: elementId, reply: reply)
    case .onboardingCompleted(let meta):
        // Track successful onboarding completion
        trackEvent("onboarding_completed", meta: meta)
    case .unknown(let meta, let name):
        // Handle unknown events
        trackEvent(name, meta: meta)
    // Handle other cases as needed
    }
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```javascript
// onboardingStarted
{
  "name": "onboarding_started",
  "meta": {
    "onboarding_id": "onboarding_123",
    "screen_cid": "welcome_screen",
    "screen_index": 0,
    "total_screens": 4
  }
}

// screenPresented

{
    "name": "screen_presented",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "interests_screen",
        "screen_index": 2,
        "total_screens": 4
    }
}

// screenCompleted

{
    "name": "screen_completed",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    },
    "params": {
        "element_id": "profile_form",
        "reply": "success"
    }
}

// secondScreenPresented

{
    "name": "second_screen_presented",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    }
}

// userEmailCollected

{
    "name": "user_email_collected",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    }
}

// onboardingCompleted

{
    "name": "onboarding_completed",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "final_screen",
        "screen_index": 3,
        "total_screens": 4
    }
}

```

</Details>