Migrate Adapty iOS SDK sang v4.0
Adapty iOS SDK 4.0 (beta) giới thiệu flows và đổi tên các API paywall tương ứng. Các API mới hoạt động với cả Flow Builder mới và Paywall Builder hiện có — không cần thay đổi cấu hình nào trên Adapty Dashboard.
Tham chiếu nhanh
| v3 | v4 |
|---|---|
Adapty.getPaywall(placementId:locale:) | Adapty.getFlow(placementId:) |
AdaptyUI.getPaywallConfiguration(forPaywall:) | AdaptyUI.getFlowConfiguration(forFlow:locale:) |
Adapty.getPaywallProducts(paywall:) | Adapty.getPaywallProducts(flow:) |
Adapty.logShowPaywall(_:) | Adapty.logShowFlow(_:) |
AdaptyPaywallController | AdaptyFlowController |
AdaptyPaywallControllerDelegate | AdaptyFlowControllerDelegate |
AdaptyUI.paywallController(with:delegate:) | AdaptyUI.flowController(with:delegate:) |
.paywall() (SwiftUI modifier) | .flow() |
AdaptyPaywallView | AdaptyFlowView |
didFailRenderingWith: / didFailRendering: | didReceiveError: |
didFinishPurchase (tùy chọn, tự động đóng khi thành công) | didFinishPurchase (bắt buộc, không tự động đóng) |
Sản phẩm package Adapty_KidsMode / AdaptyUI_KidsMode | Package trait KidsMode |
Adapty.updateAttribution(_:source:) (source: String) | Adapty.updateAttribution(_:source:) (source: AdaptyAttributionSource) |
Adapty.setIntegrationIdentifier(key:value:) | Adapty.setIntegrationIdentifier(_:) (AdaptyIntegrationIdentifier) |
Phiên bản iOS tối thiểu
Adapty iOS SDK 4.0 nâng yêu cầu deployment target tối thiểu từ iOS 13.0 lên iOS 15.0. Hãy đặt iOS Deployment Target của dự án lên 15.0 hoặc cao hơn trước khi nâng cấp.
Cài đặt: CocoaPods không còn được hỗ trợ
Adapty iOS SDK 4.0 không còn hỗ trợ CocoaPods. Hãy cài đặt SDK bằng Swift Package Manager.
Nếu dự án của bạn vẫn đang dùng CocoaPods, hãy xóa các pod Adapty và AdaptyUI khỏi Podfile, chạy pod install để dọn dẹp, sau đó thêm package trong Xcode qua File → Add Package Dependency với đường dẫn https://github.com/adaptyteam/AdaptySDK-iOS.git.
Chế độ Kids Mode: sản phẩm riêng biệt được thay thế bằng package trait
Trong v3, bạn bật Kids Mode bằng cách chọn các sản phẩm package riêng biệt Adapty_KidsMode và AdaptyUI_KidsMode rồi đổi tên các import. Kể từ 4.0.0-beta.2, các sản phẩm này đã bị loại bỏ. Kids Mode hiện là một Swift package trait có tên KidsMode trên package Adapty thông thường — khi bật lên, IDFA và AdSupport sẽ bị loại ra khỏi toàn bộ SDK trong quá trình biên dịch.
Để migrate:
-
Trong cửa sổ Choose Package Products, chọn sản phẩm Adapty và AdaptyUI thông thường thay vì Adapty_KidsMode và AdaptyUI_KidsMode.
-
Bật trait
KidsMode. Trong Xcode 26.4 trở lên, bật trait này cho dependency AdaptySDK-iOS trong phần Package Dependencies của dự án. Nếu bạn thêm Adapty dưới dạng dependency trongPackage.swift(yêu cầuswift-tools-version6.1 trở lên), hãy bật tại đó:
.package(
url: "https://github.com/adaptyteam/AdaptySDK-iOS.git",
exact: "4.0.0-beta.2",
traits: ["KidsMode"]
)
-
Đổi lại các import về module thông thường:
- import Adapty_KidsMode - import AdaptyUI_KidsMode + import Adapty + import AdaptyUI
Các phiên bản Xcode trước 26.4 không thể bật trait cho dự án Xcode từ giao diện. Trong trường hợp đó, hãy thêm một Swift package cục bộ nhỏ phụ thuộc vào Adapty với trait KidsMode được bật, rồi để app target của bạn phụ thuộc vào package đó.
Các API đã bị xóa
Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:)— đã bị xóa. Tất cả sản phẩm hiện đã bao gồm thông tin ưu đãi, vì vậy không cần thêm bước kiểm tra tính đủ điều kiện riêng nữa.AdaptyPaywallProductWithoutDeterminingOffer— đã bị xóa. Các callback trước đây truyền kiểu này (chẳng hạn nhưdidSelectProduct) nay sẽ truyềnAdaptyPaywallProduct.
Tính năng in-app purchase được quảng bá trên App Store tạm thời bị gỡ bỏ
Trong quá trình migrate sang StoreKit 2, Adapty iOS SDK 4.0 loại bỏ hỗ trợ cho in-app purchase được quảng bá trên App Store. Phương thức delegate shouldAddStorePayment(for:) và kiểu AdaptyDeferredProduct mà nó nhận đều không khả dụng trong phiên bản 4.0.
Việc gỡ bỏ này chỉ là tạm thời — hỗ trợ in-app purchase được quảng bá sẽ trở lại trong một bản phát hành 4.x sau này. Nếu ứng dụng của bạn phụ thuộc vào tính năng này, hãy tiếp tục sử dụng iOS SDK 3.x cho đến khi hỗ trợ được khôi phục.
Lấy paywalls
getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration
Các kiểu trả về thay đổi từ AdaptyPaywall / AdaptyUI.PaywallConfiguration sang AdaptyFlow / AdaptyUI.FlowConfiguration. Tham số locale được chuyển ra khỏi lệnh gọi fetch và đưa vào getFlowConfiguration:
- let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en")
- let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
+ let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")
+ let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow, locale: "en")
getPaywallProducts(paywall:) → getPaywallProducts(flow:)
getPaywallProducts hiện nhận một AdaptyFlow được trả về bởi Adapty.getFlow:
- let products = try await Adapty.getPaywallProducts(paywall: paywall)
+ let products = try await Adapty.getPaywallProducts(flow: flow)
Theo dõi lượt xem paywall
logShowPaywall(:) → logShowFlow(:)
logShowPaywall được đổi tên thành logShowFlow và nhận vào một AdaptyFlow thay vì AdaptyPaywall. Sự kiện vẫn được ghi lại theo cùng một biến thể, do đó 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 gì trên dashboard.
- try await Adapty.logShowPaywall(paywall)
+ try await Adapty.logShowFlow(flow)
Như trong 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 Flow Builder hoặc Paywall Builder — Adapty tự động theo dõi các lượt xem đó.
didFinishPurchase hiện là bắt buộc
Trong v3, didFinishPurchase là tùy chọn: nếu bạn không implement nó, paywall sẽ tự đóng sau khi mua hàng thành công. Bắt đầu từ 4.0.0-beta.2, tính năng tự động đóng mặc định này đã bị loại bỏ để flow có thể tiếp tục sau khi mua hàng thành công — ví dụ: để hiển thị các màn hình còn lại trong flow của bạn. Bây giờ bạn tự quyết định điều gì xảy ra sau khi mua: đóng màn hình, hoặc không làm gì để flow tiếp tục.
- UIKit: Những đối tượng tuân theo
AdaptyFlowControllerDelegatephải triển khaididFinishPurchase— phương thức này không còn có phần triển khai mặc định nữa. - SwiftUI: closure
didFinishPurchasecủa.flow(...)vàAdaptyFlowView(...)giờ là bắt buộc (non-optional), giống vớididFailPurchasevàdidFinishRestore.
Để giữ nguyên hành vi của v3, hãy tự dismiss màn hình:
func flowController(
_ controller: AdaptyFlowController,
didFinishPurchase product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
if !purchaseResult.isPurchaseCancelled {
controller.dismiss(animated: true)
}
}
UIKit
AdaptyPaywallController → AdaptyFlowController
Đổi tên kiểu controller và factory method:
- let controller = try AdaptyUI.paywallController(
- with: paywallConfiguration,
- delegate: self
- )
+ let controller = try AdaptyUI.flowController(
+ with: flowConfiguration,
+ delegate: self
+ )
AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate
Đổi tên protocol và cập nhật tất cả các method signature. Lưu ý rằng didSelectProduct hiện nhận AdaptyPaywallProduct thay vì AdaptyPaywallProductWithoutDeterminingOffer đã bị xóa, và didFinishPurchase phải được implement — nó không còn có default implementation nữa.
- class YourClass: AdaptyPaywallControllerDelegate {
+ class YourClass: AdaptyFlowControllerDelegate {
- func paywallControllerDidAppear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
- func paywallControllerDidDisappear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPerform action: AdaptyUI.Action) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPerform action: AdaptyUI.Action) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didSelectProduct product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didStartPurchase product: AdaptyPaywallProduct) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didStartPurchase product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishPurchase product: AdaptyPaywallProduct,
- purchaseResult: AdaptyPurchaseResult) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishPurchase product: AdaptyPaywallProduct,
+ purchaseResult: AdaptyPurchaseResult) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailPurchase product: AdaptyPaywallProduct,
- error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailPurchase product: AdaptyPaywallProduct,
+ error: AdaptyError) { }
- func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishRestoreWith profile: AdaptyProfile) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishRestoreWith profile: AdaptyProfile) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRestoreWith error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailRestoreWith error: AdaptyError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRenderingWith error: AdaptyUIError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didReceiveError error: AdaptyUIError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailLoadingProductsWith error: AdaptyError) -> Bool { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailLoadingProductsWith error: AdaptyError) -> Bool { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPartiallyLoadProducts failedIds: [String]) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPartiallyLoadProducts failedIds: [String]) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
- error: AdaptyError?) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
+ error: AdaptyError?) { }
}
SwiftUI
Modifier .paywall() → .flow()
Đổi tên modifier, cập nhật tên tham số cấu hình, và thêm closure didFinishPurchase bắt buộc:
@State var flowPresented = false // rename freely — the variable name is your choice
var body: some View {
Text("Hello, AdaptyUI!")
- .paywall(
+ .flow(
isPresented: $flowPresented,
- paywallConfiguration: paywallConfiguration,
+ flowConfiguration: flowConfiguration,
+ 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 */ },
- didFailRendering: { error in flowPresented = false }
+ didReceiveError: { error in flowPresented = false }
)
}
Callback được đổi tên này kích hoạt cho những lỗi render giống như didFailRendering trước đây, cộng thêm các lỗi runtime mới từ flow script (JavaScript exception với mã AdaptyUIError là 4105 — .jsException). Phần thân handler hiện có không cần thay đổi code — chỉ cần đổi tên tham số.
AdaptyPaywallView → AdaptyFlowView
Đổi tên view, cập nhật tham số cấu hình, thêm closure didFinishPurchase bắt buộc, và cập nhật closure didSelectProduct nếu có — giờ đây nó nhận AdaptyPaywallProduct thay cho AdaptyPaywallProductWithoutDeterminingOffer đã bị xóa:
- AdaptyPaywallView(
- paywallConfiguration: paywallConfiguration,
- didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ },
+ AdaptyFlowView(
+ flowConfiguration: flowConfiguration,
+ didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ },
+ 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 */ },
- didFailRendering: { error in /* handle the error */ }
+ didReceiveError: { error in /* handle the error */ }
)
Tài nguyên tùy chỉnh của AdaptyUI
AdaptyUICustomVideoAsset
Có hai thay đổi ảnh hưởng đến tất cả các call site hiện có:
.playerhiện nhậnAVPlayerthay vìAVQueuePlayer.- Mỗi case đều có thêm tham số
resolution: CGSize?ở cuối. Truyềnnilđể giữ nguyên hành vi hiện tại, hoặc truyền kích thước pixel thực tế để player có thể dành chỗ bố cục (tỉ lệ khung hình =width / height) trước khi video tải xong.
- case file(url: URL, preview: AdaptyUICustomImageAsset?)
- case remote(url: URL, preview: AdaptyUICustomImageAsset?)
- case player(item: AVPlayerItem, player: AVQueuePlayer, preview: AdaptyUICustomImageAsset?)
+ case file(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case remote(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case player(item: AVPlayerItem, player: AVPlayer, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
Định danh attribution và tích hợp
updateAttribution(_:source:)
Tham số source thay đổi từ kiểu String sang kiểu AdaptyAttributionSource mới, và AdaptyProfile.AttributionSource trước đây được đặt lồng nhau nay được đổi tên thành AdaptyAttributionSource ở cấp độ top-level. Sử dụng một trong các source được định nghĩa sẵn, hoặc truyền một string literal cho bất kỳ source nào khác — AdaptyAttributionSource tuân theo ExpressibleByStringLiteral, vì vậy các lệnh gọi dùng string literal hiện có vẫn tiếp tục biên dịch được.
- try await Adapty.updateAttribution(attribution, source: "adjust")
+ try await Adapty.updateAttribution(attribution, source: .adjust)
Nguồn được định sẵn: .appleAds, .adjust, .appsflyer, .branch, .tenjin. Nếu bạn lưu nguồn trong biến String, hãy bọc nó lại: AdaptyAttributionSource(rawValue: yourSource).
setIntegrationIdentifier(_:)
setIntegrationIdentifier(key:value:) được thay thế bằng một phương thức variadic nhận một hoặc nhiều giá trị AdaptyIntegrationIdentifier. Hãy dùng các factory method có sẵn thay vì chuỗi key thô:
- try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ try await Adapty.setIntegrationIdentifier(.appsflyerId(uid))
Bạn có thể đặt nhiều identifier trong một lần gọi:
try await Adapty.setIntegrationIdentifier(
.appsflyerId(uid),
.adjustDeviceId(adid)
)
Thay thế từng chuỗi key cũ bằng factory method tương ứng:
| v3 key | v4 factory |
|---|---|
"adjust_device_id" | .adjustDeviceId(_:) |
"airbridge_device_id" | .airbridgeDeviceId(_:) |
"amplitude_user_id" | .amplitudeUserId(_:) |
"amplitude_device_id" | .amplitudeDeviceId(_:) |
"appmetrica_device_id" | .appmetricaDeviceId(_:) |
"appmetrica_profile_id" | .appmetricaProfileId(_:) |
"appsflyer_id" | .appsflyerId(_:) |
"branch_id" | .branchId(_:) |
"facebook_anonymous_id" | .facebookAnonymousId(_:) |
"firebase_app_instance_id" | .firebaseAppInstanceId(_:) |
"mixpanel_user_id" | .mixpanelUserId(_:) |
"one_signal_subscription_id" | .oneSignalSubscriptionId(_:) |
"one_signal_player_id" | .oneSignalPlayerId(_:) |
"posthog_distinct_user_id" | .posthogDistinctUserId(_:) |
"pushwoosh_hwid" | .pushwooshHWID(_:) |
"tenjin_analytics_installation_id" | .tenjinAnalyticsInstallationId(_:) |