Tối ưu hóa việc tải flow & paywall trong iOS SDK

Một lần tải flow hoặc paywall đáng tin cậy trên iOS cần đảm bảo ba điều: hiển thị nhanh, trả về đúng biến thể dành cho đối tượng mục tiêu, và xử lý dự phòng linh hoạt khi mạng chậm. Các quy tắc dưới đây bao gồm thời điểm tải, caching và các mẫu fallback để đạt được điều đó.

Tip

Các quy tắc này giả định rằng Adapty.activate() và Adapty.identify() đã được thực thi xong. Xem Thứ tự gọi trong iOS SDK.

Quy tắc và những lỗi thường gặp

Làm thế nàyĐừng làm thế nàyTại sao
Fetch placement mà bạn sắp hiển thị, hoặc làm ấm cache bằng preloadFlows (SDK 4.1+).Tự tạo các lệnh gọi getFlow đồng thời khi khởi động.Việc tự prefetch hàng loạt sẽ chặn luồng chính và gây màn hình đen. preloadFlows được tối ưu cho việc này và chia sẻ một ngân sách timeout chung cho cả batch.
Gọi getFlow sau khi attribution đã có cơ hội resolve — ví dụ, 1–2 giây sau activate hoặc sau khi didLoadLatestProfile được kích hoạt.Gọi getFlow tại App.init().Attribution chưa được ghi nhận. Flow sẽ resolve theo đối tượng mặc định và bỏ qua các phân khúc cũng như cá nhân hóa ASA một cách lặng lẽ.
Đặt loadTimeout và cấu hình paywall dự phòng cho mọi placement.Chờ getFlow vô thời hạn.Không có timeout, người dùng có kết nối kém sẽ thấy màn hình trắng cho đến khi mạng phản hồi — hoặc họ sẽ đóng app.

Khi loadTimeout kích hoạt trong bất kỳ lần tải nào — kể cả getFlow đơn giản — SDK sẽ trả về biến thể đã lưu trong bộ nhớ cache nếu có, nếu không sẽ tải biến thể của đối tượng mặc định (All Users) trong thời gian còn lại. Việc nhắm mục tiêu bị mất cho yêu cầu đó, không phải bị trì hoãn: các phân khúc và đối tượng dựa trên attribution sẽ không áp dụng cho kết quả.

Xem Tải paywall và sản phẩm để tham khảo tham số fetchPolicy và loadTimeout, và Placements để chọn placement phù hợp.

Tải trước placement

Info

preloadFlows và preloadFlowsForDefaultAudience khả dụng từ SDK phiên bản 4.1 trở lên.

preloadFlows lưu flow JSON vào bộ nhớ cache trước — một request cho mỗi placement. Sau đó bạn sử dụng như bình thường: getFlow để lấy flow, getFlowConfiguration để lấy cấu hình view của nó.

fetchPolicy quyết định layer nào mà getFlow đọc trước, chứ không phải là liệu nó có thể tiếp cận cache hay không:

  • .returnCacheDataElseLoad đọc bản preload trước, chỉ ra mạng khi không có cache. .returnCacheDataIfNotExpiredElseLoad(maxAge:) hoạt động tương tự nhưng chỉ khi bản đó còn trẻ hơn maxAge.
  • Mặc định là .reloadRevalidatingCacheData — ra mạng trước, chỉ dùng bản preload khi request thất bại hoặc timeout.

Dù chọn policy nào, preload đều có ích, nhưng theo cách khác nhau: policy ưu tiên cache sẽ loại bỏ hoàn toàn request, còn policy mặc định vẫn gửi request nhưng có sẵn bản dự phòng khi cần.

Dùng cách này khi bạn biết trước những placement nào sẽ cần trong phiên nhưng chưa muốn hiển thị ngay — ví dụ, ngay sau khi activate và identify hoàn thành, cho flow đằng sau một nút mà người dùng chưa nhấn.

Tham số:

  • placementIds (bắt buộc): các placement cần preload. ID trống và trùng lặp sẽ bị bỏ qua.
  • loadTimeout (tùy chọn): thời gian chờ tính bằng giây cho toàn bộ batch, không phải cho từng placement. Mặc định là 5 giây, và các giá trị dưới 1 giây sẽ được nâng lên 1 giây.

Một số điều cần lưu ý về hành vi:

  • Phương thức chỉ báo lỗi sau khi đã thử tất cả các placement, và lỗi trả về tổng hợp các lỗi từng placement riêng lẻ. Lỗi ở một placement không làm dừng các placement khác.
  • Nếu một placement hết thời gian chờ hoặc gặp lỗi mạng, SDK sẽ dùng biến thể đối tượng mặc định cho placement đó. Các lỗi khác được báo cáo nguyên trạng.
  • Nếu timeout xảy ra trước khi hoàn tất lần tải theo đối tượng mục tiêu, SDK vẫn thử biến thể đối tượng mặc định trong khoảng thời gian còn lại.
  • Preloading chỉ làm ấm cache. Nó không trả về nội dung — bạn vẫn phải gọi getFlow để hiển thị.

Những gì preload bao gồm

Một flow hiển thị trên màn hình theo từng lớp. Preload bao gồm lớp đầu tiên, giống như getFlow thực hiện:

LớpĐược tải bởiĐược làm ấm bởi preload
Flow JSON — biến thể được chọn, ID sản phẩm và remote configgetFlowCó
Bố cục giao diện — cấu trúc, kiểu dáng và văn bản của màn hìnhgetFlowConfigurationKhông
Hình ảnh, bao gồm khung tĩnh thay thế cho phần tử videogetFlowConfiguration, chạy nềnKhông
Tệp videoTrình phát hệ thống, khi màn hình renderKhông được SDK lưu cache

getFlowConfiguration phải chờ layout tải xong, nên lần đầu tiên yêu cầu một layout nhất định sẽ tốn một round trip ngay cả sau khi đã preload. SDK sau đó lưu layout đó vào bộ nhớ đệm trên đĩa, bộ nhớ này tồn tại qua các lần khởi động lại ứng dụng và được đọc trước bất kỳ lần gọi mạng nào, nên chi phí chỉ rơi vào lần yêu cầu đầu tiên chứ không phải mỗi lần. Sau khi SDK đã có layout, nó bắt đầu cache ảnh độc lập với lần gọi đó: không chặn màn hình, và không có callback, delegate method hay lỗi nào báo lại khi hoàn tất.

Tìm hiểu placement nào bị lỗi

Lỗi được ném ra là một AdaptyError duy nhất bao phủ toàn bộ batch, với mã networkFailed (2005). Để xem từng lỗi riêng lẻ, hãy đọc thuộc tính preloadErrors của nó — một dictionary có khóa là placement ID:

do {
    try await Adapty.preloadFlows(placementIds: ["onboarding", "main_paywall"])
} catch {
    for (placementId, placementError) in error.preloadErrors ?? [:] {
        // log or retry the individual placement
    }
}

preloadErrors là nil đối với bất kỳ lỗi nào không đến từ lệnh gọi preload, vì vậy hãy coi giá trị nil là “không phải lỗi preload” thay vì “không có lỗi”.

Bỏ qua phân khúc đối tượng

Để làm ấm bộ nhớ cache mà không cần chờ phân khúc đối tượng, hãy dùng biến thể default-audience:

try await Adapty.preloadFlowsForDefaultAudience(placementIds: ["main_paywall"])

Hiển thị media màn hình đầu từ app bundle

Một flow tải hình ảnh và video từ Adapty. Để hiển thị media của màn hình đầu tiên ngay lập tức, hãy phục vụ nó từ app bundle thay thế. Đây là cách tốt để tái sử dụng media bạn đã đóng gói sẵn, chẳng hạn như hình ảnh của một onboarding gốc hiện có.

  1. Trong Flow & Paywall Builder, đặt custom media ID trên hình ảnh hoặc video. File bạn tải lên đó vẫn là file dự phòng.
  2. Thêm file vào app bundle của bạn.
  3. Khi bạn gọi getFlowConfiguration, truyền file đã đóng gói cho ID đó thông qua assetsResolver:
// "welcome_video" is the custom media ID set in the Flow & Paywall Builder
let bundledAssets: [String: AdaptyCustomAsset] = [
    "welcome_video": .video(
        .file(
            url: Bundle.main.url(forResource: "welcome", withExtension: "mp4")!,
            preview: .uiImage(value: UIImage(named: "welcome_poster")!),
            resolution: CGSize(width: 1080, height: 1920)
        )
    ),
]

let flowConfig = try await AdaptyUI.getFlowConfiguration(
    forFlow: flow,
    assetsResolver: bundledAssets
)

Các tệp đã đóng gói sẽ làm tăng dung lượng tải xuống của ứng dụng, vì vậy hãy chỉ đóng gói các media mà người dùng nhìn thấy đầu tiên.

Media không được đóng gói vẫn hiển thị ngay lập tức: cấu hình view mang theo một bản sao độ phân giải thấp nhỏ của mỗi hình ảnh, bao gồm cả khung tĩnh của video, và hiển thị nó cho đến khi tệp đầy đủ được tải xong.

Để xem tài liệu tham khảo đầy đủ về assetsResolver, hãy xem Tùy chỉnh assets.

Điều chỉnh cho kết nối kém

Đối với các thị trường có kết nối kém liên tục (vùng nông thôn, phương tiện công cộng, khu vực bị ảnh hưởng bởi định tuyến):

  • Đặt fetchPolicy: .returnCacheDataElseLoad cho mọi lần fetch ngoại trừ lần đầu tiên.
  • Cấu hình paywall dự phòng cho mọi placement trong Adapty Dashboard.
  • Đặt loadTimeout từ 3–5 giây và chấp nhận fallback khi timeout kích hoạt.
  • Đừng chặn việc hiển thị flow vào getProfile(). Gọi getFlow độc lập để hồ sơ người dùng chậm không chặn giao diện.