Migrate Adapty Kotlin Multiplatform SDK sang v4.1
Adapty Kotlin Multiplatform SDK 4.1 là bản phát hành ổn định đầu tiên của dòng 4.x — 4.0 chỉ được phát hành dưới dạng beta, vì vậy nếu bạn đang dùng 3.x, hãy migrate thẳng lên 4.1. Hướng dẫn này bao gồm toàn bộ quá trình migration: các flow được giới thiệu trong 4.0 và các thay đổi của 4.1.
Phiên bản 4.x giới thiệu flow và đổi tên các API paywall cho phù hợp. Các API mới hoạt động với cả Flow & Paywall Builder lẫn Paywall Builder cũ — không cần thay đổi cấu hình trên Adapty Dashboard. Ngoài ra, phiên bản 4.1 chuyển Adapty Attribution sang dạng opt-in, đổi tên các API attribution bên ngoài và loại gói đăng ký sản phẩm, đồng thời bổ sung tính năng in-app purchase được quảng bá trên App Store.
Bạn đang chuyển từ bản beta 4.0? Hãy cập nhật phiên bản, sau đó chỉ có năm mục cần lưu ý: Adapty Attribution mặc định bị tắt, các API external attribution đã được đổi tên, AdaptyPaywallProductSubscription → AdaptyProductSubscription, In-app purchase được quảng bá trên App Store, và chọn một layout cụ thể. hasViewConfiguration cũng đã trở lại trên flow model.
Tài liệu tham khảo nhanh
| v3 | v4.1 |
|---|---|
| Adapty Attribution được bật tự động | mặc định bị tắt — bật bằng .withAdaptyAttributionEnabled(true) |
Adapty.getPaywall(placementId, locale) | Adapty.getFlow(placementId) |
Adapty.getPaywallForDefaultAudience(placementId, locale) | Adapty.getFlowForDefaultAudience(placementId) |
Adapty.getPaywallProducts(paywall) | Adapty.getPaywallProducts(flow) |
Adapty.logShowPaywall(paywall) | Adapty.logShowFlow(flow) |
AdaptyPaywall | AdaptyFlow |
AdaptyUI.createPaywallView(paywall, ...) | AdaptyUI.createFlowView(flow, ...) |
AdaptyUI.createNativePaywallView(...) → AdaptyNativePaywallView | AdaptyUI.createNativeFlowView(...) → AdaptyNativeFlowView |
AdaptyUIPaywallView | AdaptyUIFlowView |
AdaptyUI.presentPaywallView(view) / dismissPaywallView(view) | AdaptyUI.presentFlowView(view) / dismissFlowView(view) |
AdaptyUI.setPaywallsEventsObserver(observer) | AdaptyUI.setFlowsEventsObserver(observer) |
AdaptyUI.registerPaywallEventsListener / unregisterPaywallEventsListener | AdaptyUI.registerFlowEventsListener / unregisterFlowEventsListener |
AdaptyUIPaywallsEventsObserver | AdaptyUIFlowsEventsObserver |
AdaptyUIPaywallPlatformView(paywall, ...) | AdaptyUIFlowPlatformView(flow, ...) |
paywallViewDidPerformAction, paywallViewDidAppear và các callback paywallView... khác | flowViewDidPerformAction, flowViewDidAppear và các callback flowView... khác |
paywallViewDidFailRendering | flowViewDidReceiveError |
Adapty.updateAttribution(attribution, source) với source kiểu String | Adapty.updateExternalAttribution(attribution, provider) với AdaptyExternalAttributionProvider |
provider truyền dưới dạng chuỗi, ví dụ "adjust" | AdaptyExternalAttributionProvider, ví dụ AdaptyExternalAttributionProvider.ADJUST |
AdaptyProfile.appliedAttributionSources: List<String> | AdaptyProfile.appliedExternalAttributionProviders: List<AdaptyExternalAttributionProvider> |
AdaptyPaywallProductSubscription | AdaptyProductSubscription |
| In-app purchase được quảng bá hoàn tất tự động, không có cách nào để chặn | OnPromotedPurchaseListener và Adapty.makePromotedPurchase(product) trao quyền hoàn tất cho ứng dụng của bạn |
AdaptyPaywallProduct giữ nguyên tên — sản phẩm vẫn thuộc về một flow, và getPaywallProducts cũng giữ nguyên tên, giờ nhận vào một AdaptyFlow. Các phương thức getFlow và getFlowForDefaultAudience không còn nhận tham số locale nữa — thay vào đó hãy truyền nó vào createFlowView. Các API mua hàng và hồ sơ người dùng (makePurchase, restorePurchases, getProfile, identify, updateProfile) và setFallback giữ nguyên signature, nhưng file fallback phải được tải lại — xem Fallback files. Các phương thức onboarding vẫn hoạt động nhưng đã bị deprecated — xem Onboarding API deprecation. Một số hành vi mặc định đã thay đổi — xem Default behavior changes.
Cài đặt
Cập nhật phiên bản và đồng bộ dự án:
[versions]
adapty-kmp = "<the latest SDK version>"
[libraries]
adapty-kmp = { module = "io.adapty:adapty-kmp", version.ref = "adapty-kmp" }
adapty-kmp-ui = { module = "io.adapty:adapty-kmp-ui", version.ref = "adapty-kmp" }
Module adapty-kmp-ui chỉ cần thiết nếu bạn hiển thị flow và paywall bằng lớp Compose Multiplatform (view.present()). Xem Cài đặt Adapty SDK để biết hướng dẫn thiết lập đầy đủ.
Các SDK Adapty native bên dưới đã được nâng lên phiên bản 4.x trên cả hai nền tảng và được giải quyết tự động — không cần thay đổi cấu hình build. Deployment target iOS vẫn giữ nguyên là 15.0, không thay đổi trong bản phát hành này.
⚠️ Adapty Attribution mặc định bị tắt
Nếu bạn sử dụng Adapty Attribution và cập nhật lên SDK 4.1 mà không bật tính năng này, mọi thứ sẽ lặng lẽ ngừng hoạt động — các lượt cài đặt không được ghi nhận, và không có bất kỳ cảnh báo nào.
Trong các phiên bản trước, SDK tự động đăng ký lượt cài đặt cho Adapty Attribution. Kể từ SDK phiên bản 4.1, tính năng này bị tắt theo mặc định: SDK không đăng ký lượt cài đặt, listener được đặt bằng setOnInstallationDetailsListener sẽ không bao giờ kích hoạt, và getCurrentInstallationStatus trả về AdaptyInstallationStatus.Determined.NotAvailable.
Nếu bạn sử dụng Adapty Attribution, hãy bật tính năng này khi khởi động SDK:
val config = AdaptyConfig
.Builder("PUBLIC_SDK_KEY")
+ .withAdaptyAttributionEnabled(true)
.build()
Adapty.activate(configuration = config)
Nếu bạn không sử dụng Adapty Attribution, không cần thay đổi gì.
Tải flows
getPaywall → getFlow
Kiểu trả về thay đổi từ AdaptyPaywall sang AdaptyFlow, và tham số locale chuyển từ lệnh gọi fetch sang createFlowView; đối với các paywall tùy chỉnh, tất cả các locale được trả về trong flow.remoteConfigs:
- Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en")
- .onSuccess { paywall ->
- // use the paywall
+ Adapty.getFlow("YOUR_PLACEMENT_ID")
+ .onSuccess { flow ->
+ AdaptyUI.createFlowView(flow = flow, locale = "en")
}
.onError { error ->
// handle the error
}
locale vẫn là tùy chọn trong createFlowView: bỏ qua nó thì view sẽ hiển thị bằng en, hoặc theo ngôn ngữ mặc định của flow khi flow không có en. Xem Localizations và mã locale.
getPaywallForDefaultAudience được đổi tên tương tự:
- Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en")
+ Adapty.getFlowForDefaultAudience("YOUR_PLACEMENT_ID")
getPaywallProducts(paywall) → getPaywallProducts(flow)
getPaywallProducts giữ nguyên tên nhưng bây giờ nhận vào một AdaptyFlow:
- Adapty.getPaywallProducts(paywall)
+ Adapty.getPaywallProducts(flow)
.onSuccess { products ->
// use the products
}
Tệp dự phòng
Định dạng tệp dự phòng đã thay đổi trong SDK v4. Tải tệp mới từ Placements > Fallbacks và đóng gói vào ứng dụng của bạn.
Mô hình dữ liệu
getFlow trả về một AdaptyFlow thay vì AdaptyPaywall, và cấu trúc object đã thay đổi:
Thuộc tính v3 AdaptyPaywall | Thuộc tính v4 AdaptyFlow | Hành động |
|---|---|---|
remoteConfig: AdaptyRemoteConfig? (đơn) | remoteConfigs: List<AdaptyRemoteConfig> | Một flow mang một remote config cho mỗi ngôn ngữ đã cấu hình. Đọc cái khớp với người dùng: flow.remoteConfigs.firstOrNull { it.locale == "en" }. |
| (mới) | paywalls: List<AdaptyFlowPaywall> | Mỗi mục là một biến thể paywall trong flow, với name, variationId và productIdentifiers riêng. Các phương thức web paywall nhận một AdaptyFlowPaywall — xem Phương thức web paywall. |
productIdentifiers | đã chuyển | Định danh sản phẩm giờ nằm trên mỗi biến thể: flow.paywalls[i].productIdentifiers. Để tải sản phẩm, tiếp tục gọi getPaywallProducts(flow). |
hasViewConfiguration | giữ nguyên | Cho biết flow có mang layout mà AdaptyUI có thể render hay không. Thuộc tính này vắng mặt trong bản 4.0 beta và đã trở lại ở 4.1 — nếu bạn đã xóa các kiểm tra cho bản beta, bạn có thể dùng lại. false nghĩa là flow không mang layout, vì vậy hãy xử lý nó như remote-config only. Bạn cũng có thể gọi createFlowView và xử lý lỗi (xem Hiển thị flow). |
hasViewConfiguration cũng có trên AdaptyOnboarding, không thay đổi.
Phương thức Web paywall
openWebPaywall và createWebPaywallUrl giữ nguyên tên, nhưng tham số paywall được thay thế bằng tham số flowPaywall nhận một AdaptyFlowPaywall — một trong các biến thể trong flow.paywalls. Bạn vẫn có thể truyền vào một AdaptyPaywallProduct thay thế:
- Adapty.openWebPaywall(paywall = paywall)
+ flow.paywalls.firstOrNull()?.let { flowPaywall ->
+ Adapty.openWebPaywall(flowPaywall = flowPaywall)
+ }
Theo dõi lượt xem flow
logShowPaywall → logShowFlow
logShowPaywall được đổi tên thành logShowFlow và hiện nhận vào một AdaptyFlow. Sự kiện vẫn được ghi lại theo cùng một biến thể, vì vậy các chỉ số funnel và A/B test hiện có vẫn hoạt động bình thường mà không cần thay đổi trên dashboard.
- Adapty.logShowPaywall(paywall)
+ Adapty.logShowFlow(flow)
Tương tự như v3, bạn không cần gọi phương thức này khi hiển thị các flow hoặc paywall được render bởi Adapty — Adapty sẽ tự động theo dõi những lượt xem đó.
Hiển thị flows
createPaywallView → createFlowView
Đổi tên phương thức factory và truyền vào AdaptyFlow. Kiểu view trả về được đổi tên từ AdaptyUIPaywallView thành AdaptyUIFlowView, nhưng các phương thức (present, dismiss) và các tham số tùy chọn (loadTimeout, preloadProducts, customTags, customTimers, customAssets, productPurchaseParams) vẫn giữ nguyên. Có một tham số tùy chọn mới: locale, thay thế cho locale bạn từng truyền vào getPaywall — xem Fetching flows.
customTimers vẫn tồn tại, nhưng nó chỉ ảnh hưởng đến các paywall được tạo bằng Paywall Builder cũ. Đồng hồ đếm ngược của một flow chạy theo hành vi được thiết lập trong Flow & Paywall Builder, vì vậy flow sẽ bỏ qua bất kỳ giá trị nào bạn truyền vào đây.
- AdaptyUI.createPaywallView(paywall)
+ AdaptyUI.createFlowView(flow)
.onSuccess { view ->
view.present()
}
.onError { error ->
// handle the error
}
Nếu bạn không sử dụng Compose Multiplatform, phương thức factory gốc cũng được đổi tên tương tự:
- AdaptyUI.createNativePaywallView(paywall)
+ AdaptyUI.createNativeFlowView(flow)
createFlowView trả về AdaptyResult.Error nếu flow không có view được cấu hình, vì vậy bạn có thể bỏ kiểm tra hasViewConfiguration của v3 và thay bằng xử lý lỗi:
- if (paywall.hasViewConfiguration) {
- AdaptyUI.createPaywallView(paywall)
- .onSuccess { view -> view.present() }
- }
+ AdaptyUI.createFlowView(flow)
+ .onSuccess { view -> view.present() }
+ .onError { error ->
+ // the flow has no view configured, or view creation failed
+ }
Flow view chỉ dùng được một lần: sau khi bạn gọi dismiss(), view sẽ bị hủy, vì vậy hãy gọi createFlowView lại để hiển thị flow thêm một lần nữa.
Xử lý sự kiện
Observer sự kiện được đổi tên từ AdaptyUIPaywallsEventsObserver thành AdaptyUIFlowsEventsObserver, và các callback của nó thay đổi tiền tố paywallView thành flowView. Nội dung các handler hiện có không cần thay đổi code — chỉ cần đổi tên kiểu và các override:
- AdaptyUI.setPaywallsEventsObserver(object : AdaptyUIPaywallsEventsObserver {
- override fun paywallViewDidFinishPurchase(
- view: AdaptyUIPaywallView,
+ AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
+ override fun flowViewDidFinishPurchase(
+ view: AdaptyUIFlowView,
product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
// custom logic after purchase
}
})
Một callback cũng được đổi tên: paywallViewDidFailRendering thành flowViewDidReceiveError. Callback này kích hoạt cho các lỗi render như trước, cộng thêm các lỗi runtime không liên quan đến mua hàng:
- override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) {}
+ override fun flowViewDidReceiveError(view: AdaptyUIFlowView, error: AdaptyError) {}
Xem Xử lý sự kiện flow & paywall để biết danh sách đầy đủ các callback.
Compose platform view
Nếu bạn nhúng view bằng composable Compose Multiplatform, AdaptyUIPaywallPlatformView(paywall, ...) được đổi tên thành AdaptyUIFlowPlatformView(flow, ...). Các callback sự kiện vẫn giữ tên onDid..., ngoại trừ onDidFailRendering được đổi thành onDidReceiveError:
- AdaptyUIPaywallPlatformView(
- paywall = paywall,
+ AdaptyUIFlowPlatformView(
+ flow = flow,
onDidFinishPurchase = { view, product, result -> /* ... */ },
)
Giống như trong v3, các callback bạn truyền vào đây (và bất kỳ observer nào được đăng ký qua registerFlowEventsListener) chạy bổ sung vào global observer, không thay thế nó — callback của bạn quan sát một sự kiện; nó không thay thế hành vi mặc định toàn cục. Hãy lưu ý các thay đổi mặc định: ví dụ, hành vi mặc định toàn cục không còn tự động đóng view sau khi mua hàng nữa.
API Mới
AdaptyUI.setObserverModeResolver(...)vớiAdaptyUIObserverModeResolver— xử lý các giao dịch mua và khôi phục được khởi tạo từ các flow khi SDK chạy ở chế độ Observer. Trước đây tính năng này chỉ có trong SDK iOS và Android gốc. Xem Hiển thị flow trong chế độ Observer.AdaptyUI.setSystemRequestsHandler(...)vớiAdaptyUISystemRequestsHandler— dành riêng cho các yêu cầu hệ thống từ một flow (lời nhắc cấp quyền của hệ điều hành và yêu cầu đánh giá ứng dụng). Hiện tại các flow chưa kích hoạt những yêu cầu này, vì vậy bạn không cần đăng ký handler.- Callback tùy chọn mới
flowViewDidReceiveAnalyticEventbáo cáo các sự kiện analytics từ một flow, bắt đầu bằng sự kiện xem màn hình cho mỗi màn hình người dùng mở. Xem Theo dõi lượt xem màn hình flow. AdaptyUI.openWebUrl(url, openIn)vàAdaptyUI.requestAppReview()— hỗ trợ xử lýOpenUrlActionmặc định vàhandleAppReviewRequestmặc định, vì vậy các URL và lời nhắc đánh giá ứng dụng được xử lý ngay lập tức theo cách gốc. Chỉ gọi trực tiếp khi bạn ghi đè các giá trị mặc định đó.AdaptyUIFlowView.locale— báo cáo bản địa hóa mà view được xây dựng với, giúp bạn biết người dùng thực sự đang thấy bản nào.AdaptyConfig.ServerCluster.CN— tùy chọn cụm máy chủ mới bên cạnhDEFAULTvàEU, để kết nối ứng dụng của bạn với máy chủ China của Adapty.
Đổi tên API attribution bên ngoài
Từ SDK phiên bản 4.1, các API dùng để truyền dữ liệu attribution từ nhà cung cấp bên ngoài (Adjust, AppsFlyer, Branch, Tenjin, Apple Ads hoặc nhà cung cấp tùy chỉnh) được đổi tên để khớp với các native SDK, và nhà cung cấp thay đổi từ kiểu chuỗi sang kiểu dữ liệu. Không có alias deprecated nào, vì vậy các call site hiện có sẽ ngừng biên dịch cho đến khi bạn cập nhật chúng.
updateAttribution → updateExternalAttribution
Phương thức được đổi tên, tham số source của nó được đổi thành provider, và tham số đó hiện nhận AdaptyExternalAttributionProvider thay vì String:
- Adapty.updateAttribution(attribution, "adjust")
+ Adapty.updateExternalAttribution(attribution, AdaptyExternalAttributionProvider.ADJUST)
Dữ liệu attribution vẫn là Map<String, Any>.
Lời gọi trả về khi backend chấp nhận dữ liệu để xử lý bất đồng bộ. Kết quả thành công không có nghĩa là dữ liệu đã được áp dụng vào hồ sơ người dùng.
AdaptyExternalAttributionProvider
Provider hiện là một kiểu dữ liệu với các giá trị định sẵn: APPLE_ADS, ADJUST, APPSFLYER, BRANCH, TENJIN, và CUSTOM. Đối với bất kỳ provider nào khác, hãy khởi tạo từ định danh của nó:
AdaptyExternalAttributionProvider("your_provider")
Khởi tạo trực tiếp cũng hỗ trợ các provider mà Adapty thêm vào sau bản phát hành SDK này — định danh sẽ được gửi nguyên vẹn đến backend thay vì bị chuyển thành một giá trị không xác định. Khoảng trắng xung quanh sẽ được tự động loại bỏ.
Mỗi giá trị được định nghĩa sẵn đều bao gồm cùng một định danh mà bạn đã truyền vào updateAttribution trước đó: AdaptyExternalAttributionProvider.APPLE_ADS.value là apple_search_ads, và các giá trị còn lại dùng tên viết thường của chính chúng.
AdaptyProfile.appliedAttributionSources → appliedExternalAttributionProviders
Thuộc tính profile liệt kê các nhà cung cấp attribution đã được áp dụng cho hồ sơ người dùng được đổi tên, và kiểu phần tử của nó cũng thay đổi tương ứng:
- if (profile.appliedAttributionSources.contains("apple_search_ads")) {
+ if (profile.appliedExternalAttributionProviders.contains(AdaptyExternalAttributionProvider.APPLE_ADS)) {
// Apple Ads attribution has been applied
}
AdaptyPaywallProductSubscription → AdaptyProductSubscription
Kiểu dữ liệu chi tiết gói đăng ký được đổi tên, vì các sản phẩm được quảng bá giờ dùng chung kiểu này. Chỉ có tên thay đổi — mọi thuộc tính vẫn giữ nguyên tên và kiểu dữ liệu:
- val subscription: AdaptyPaywallProductSubscription? = product.subscription
+ val subscription: AdaptyProductSubscription? = product.subscription
In-app purchase được quảng bá trên App Store
SDK 4.1 chuyển tiếp in-app purchase được quảng bá trên trang sản phẩm App Store của bạn đến ứng dụng trên iOS. Các phiên bản trước sẽ tự động hoàn tất giao dịch mua như vậy và không cho ứng dụng cơ hội can thiệp. Từ phiên bản 4.1, giao dịch mua sẽ chờ code của bạn xử lý, vì vậy bạn cần thực hiện thay đổi này ngay cả khi trước đây bạn chưa từng đụng đến tính năng quảng bá in-app purchase.
Để hỗ trợ promoted purchases, hãy đăng ký OnPromotedPurchaseListener và hoàn tất giao dịch mua bằng cách truyền sản phẩm vào Adapty.makePromotedPurchase. Nếu không có listener được đăng ký, giao dịch mua sẽ bị treo thay vì hoàn tất: người dùng nhấn Buy trên trang App Store của bạn nhưng không có gì xảy ra trong ứng dụng. Hãy đăng ký listener càng sớm càng tốt — xem In-app purchases từ App Store để biết thời điểm và ví dụ đầy đủ.
Chọn layout cụ thể
createFlowView, createNativeFlowView, và AdaptyUIFlowPlatformView nhận thêm một tham số tùy chọn mới là customLayoutId. Truyền tham số này để render một layout cụ thể trong cấu hình layout của flow thay vì layout mà SDK tự động chọn dựa trên loại thiết bị và kích thước màn hình. Flow & Paywall Builder chưa hỗ trợ gán ID layout tùy chỉnh, vì vậy hãy để trống tham số này:
AdaptyUI.createFlowView(flow, customLayoutId = "tablet_landscape")
Nếu không có layout nào khớp với ID, flow sẽ tải mà không có cấu hình view. Tham số này là tùy chọn và mặc định là null, vì vậy các lệnh gọi hiện có sẽ không bị ảnh hưởng.
Thay đổi hành vi mặc định
Những thay đổi này không gây ra lỗi biên dịch, vì vậy hãy kiểm tra chúng ở runtime:
- Hoàn tất mua hàng: Trong v3,
paywallViewDidFinishPurchasemặc định sẽ đóng view sau mọi kết quả mua hàng ngoại trừAdaptyPurchaseResult.UserCanceled. Trong v4,flowViewDidFinishPurchasemặc định là no-op, tức là flow vẫn mở sau khi mua hàng cho đến khi bạn tự đóng nó — giống hành vi trên iOS. Nếu bạn phụ thuộc vào tính năng tự đóng đó, hãy tự gọiview.dismiss()sau khi mua hàng xong. - Nút back hệ thống Android: Trong v3,
paywallViewDidPerformActionmặc định sẽ đóng view với cảCloseActionlẫnAndroidSystemBackAction. Trong v4, hành vi mặc định chỉ xử lýCloseAction— nút back hệ thống không còn tự đóng flow nữa, giống iOS, nơi flow không thể bị đóng bằng thao tác hệ thống. Hãy cung cấp cho người dùng cách thoát rõ ràng (nút Close hoặc hành độngon_device_back), hoặc tự đóng view trongflowViewDidPerformAction. - Lỗi view: Trong v3,
paywallViewDidFailRenderingmặc định không làm gì. Trong v4,flowViewDidReceiveErrormặc định sẽ đóng view — hãy override nó nếu bạn muốn giữ view mở hoặc xử lý lỗi theo cách khác. - View chỉ dùng một lần: Sau khi gọi
dismiss(), view sẽ bị hủy. Gọi lạicreateFlowViewđể hiển thị flow thêm một lần nữa.
API onboarding đã bị ngừng hỗ trợ
API onboarding cũ đã bị ngừng hỗ trợ trong v4, thay thế bằng Flow & Paywall Builder. Tính năng này vẫn hoạt động, nhưng sẽ bị xóa trong một bản phát hành tương lai, vì vậy hãy lên kế hoạch migration các onboarding của bạn sang Flow & Paywall Builder.
Các ký hiệu đã bị ngừng hỗ trợ: getOnboarding, getOnboardingForDefaultAudience, AdaptyUI.createOnboardingView, AdaptyUI.createNativeOnboardingView, và AdaptyUIOnboardingsEventsObserver.