将 Adapty iOS SDK 迁移至 v. 4.0

Adapty iOS SDK 4.0(测试版)引入了 flows 功能,并相应重命名了付费墙 API。新 API 同时兼容全新的 Flow Builder 和现有的付费墙编辑工具——无需在 Adapty 看板侧进行任何配置变更。

快速参考

v3v4
Adapty.getPaywall(placementId:locale:)Adapty.getFlow(placementId:)
AdaptyUI.getPaywallConfiguration(forPaywall:)AdaptyUI.getFlowConfiguration(forFlow:locale:)
Adapty.getPaywallProducts(paywall:)Adapty.getPaywallProducts(flow:)
Adapty.logShowPaywall(_:)Adapty.logShowFlow(_:)
AdaptyPaywallControllerAdaptyFlowController
AdaptyPaywallControllerDelegateAdaptyFlowControllerDelegate
AdaptyUI.paywallController(with:delegate:)AdaptyUI.flowController(with:delegate:)
.paywall()(SwiftUI 修饰符).flow()
AdaptyPaywallViewAdaptyFlowView
didFailRenderingWith: / didFailRendering:didReceiveError:
Adapty.updateAttribution(_:source:)source: StringAdapty.updateAttribution(_:source:)source: AdaptyAttributionSource
Adapty.setIntegrationIdentifier(key:value:)Adapty.setIntegrationIdentifier(_:)AdaptyIntegrationIdentifier

安装:不再支持 CocoaPods

Adapty iOS SDK 4.0 已放弃对 CocoaPods 的支持。请改用 Swift Package Manager 安装 SDK。

如果你的项目仍在使用 CocoaPods,请从 Podfile 中移除 AdaptyAdaptyUI pods,运行 pod install 将其清理,然后在 Xcode 中通过 File → Add Package Dependency,使用 https://github.com/adaptyteam/AdaptySDK-iOS.git 添加该包。

已移除的 API

  • Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:) — 已移除。所有产品现在均包含优惠信息,因此不再需要单独的资格验证步骤。
  • AdaptyPaywallProductWithoutDeterminingOffer — 已移除。之前传递此类型的回调(例如 didSelectProduct)现在改为传递 AdaptyPaywallProduct

获取付费墙

getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration

返回类型从 AdaptyPaywall / AdaptyUI.PaywallConfiguration 变更为 AdaptyFlow / AdaptyUI.FlowConfigurationlocale 参数从 fetch 调用中移出,改为在 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 现在接受由 Adapty.getFlow 返回的 AdaptyFlow

- let products = try await Adapty.getPaywallProducts(paywall: paywall)
+ let products = try await Adapty.getPaywallProducts(flow: flow)

追踪付费墙展示

logShowPaywall(:) → logShowFlow(:)

logShowPaywall 已重命名为 logShowFlow,现在接受 AdaptyFlow 而非 AdaptyPaywall。事件仍会记录在相同的实验变体下,因此现有的漏斗和 A/B 测试数据图表无需修改看板即可继续正常使用。

- try await Adapty.logShowPaywall(paywall)
+ try await Adapty.logShowFlow(flow)

与 v3 一样,在显示由流程编辑工具付费墙编辑工具渲染的流程或付费墙时,无需调用此方法——Adapty 会自动追踪这些浏览记录。

UIKit

AdaptyPaywallController → AdaptyFlowController

重命名控制器类型和工厂方法:

- let controller = try AdaptyUI.paywallController(
-     with: paywallConfiguration,
-     delegate: self
- )
+ let controller = try AdaptyUI.flowController(
+     with: flowConfiguration,
+     delegate: self
+ )

AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate

重命名该协议并更新所有方法签名。注意,didSelectProduct 现在接收 AdaptyPaywallProduct,而不再使用已移除的 AdaptyPaywallProductWithoutDeterminingOffer

- 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

.paywall() 修饰符 → .flow()

重命名修饰符并更新配置参数名称:

 @State var flowPresented = false // 变量名可以自由重命名

 var body: some View {
     Text("Hello, AdaptyUI!")
-        .paywall(
+        .flow(
             isPresented: $flowPresented,
-            paywallConfiguration: paywallConfiguration,
+            flowConfiguration: flowConfiguration,
             didFailPurchase: { product, error in /* 处理错误 */ },
             didFinishRestore: { profile in /* 检查访问等级并关闭 */ },
             didFailRestore: { error in /* 处理错误 */ },
-            didFailRendering: { error in flowPresented = false }
+            didReceiveError: { error in flowPresented = false }
         )
 }

重命名后的回调触发时机与原来的 didFailRendering 相同,同时新增了流程脚本产生的运行时错误(AdaptyUIError 错误码 4105——.jsException 对应的 JavaScript 异常)。现有的处理器代码无需修改——只需重命名参数即可。

AdaptyPaywallView → AdaptyFlowView

将视图重命名,更新配置参数,并更新所有 didSelectProduct 闭包——该闭包现在接收 AdaptyPaywallProduct,而非已移除的 AdaptyPaywallProductWithoutDeterminingOffer

- AdaptyPaywallView(
-     paywallConfiguration: paywallConfiguration,
-     didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ },
+ AdaptyFlowView(
+     flowConfiguration: flowConfiguration,
+     didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ },
     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 */ }
 )

AdaptyUI 自定义资源

AdaptyUICustomVideoAsset

以下两项变更会影响所有已有的调用位置:

  • .player 现在接受 AVPlayer 而非 AVQueuePlayer
  • 每个 case 新增了末尾参数 resolution: CGSize?。传入 nil 可保持现有行为;传入实际像素尺寸后,播放器可在视频加载前预留布局空间(宽高比 = width / height)。
- 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?)

归因与集成标识符

updateAttribution(_:source:)

source 参数的类型从 String 改为新的 AdaptyAttributionSource 类型,原来嵌套的 AdaptyProfile.AttributionSource 被重命名为顶层的 AdaptyAttributionSource。可以使用预定义的来源之一,也可以传入字符串字面量表示其他来源——AdaptyAttributionSource 遵循 ExpressibleByStringLiteral,因此现有的字符串字面量调用无需修改即可继续编译。

- try await Adapty.updateAttribution(attribution, source: "adjust")
+ try await Adapty.updateAttribution(attribution, source: .adjust)

预定义来源:.appleAds.adjust.appsflyer.branch.tenjin。如果来源存储在 String 变量中,请用 AdaptyAttributionSource(rawValue: yourSource) 包装。

setIntegrationIdentifier(_:)

setIntegrationIdentifier(key:value:) 已被替换为一个可变参数方法,支持传入一个或多个 AdaptyIntegrationIdentifier 值。请使用预定义的工厂方法,而非原始字符串键:

- try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ try await Adapty.setIntegrationIdentifier(.appsflyerId(uid))

你可以在单次调用中设置多个标识符:

try await Adapty.setIntegrationIdentifier(
    .appsflyerId(uid),
    .adjustDeviceId(adid)
)

将每个旧的键字符串替换为其工厂方法:

v3 keyv4 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(_:)