メインコンテンツまでスキップ

从旧版 AppCoins SDK 升级

本指南介绍如何从旧版 AppCoins iOS SDK 升级到当前版本。新版本与 StoreKit 2 的公开接口保持一致,因此需要重命名若干属性、以 Transaction 取代 Purchase 类型,并完全移除 PurchaseIntent / Purchase.updates 流程。

概览

当前 SDK 版本的主要变更如下:

  • Purchase 重命名为 Transaction —— Purchase.allPurchase.unfinishedPurchase.latest 现在分别为 Transaction.allTransaction.unfinishedTransaction.latest(for:)
  • 移除 PurchaseIntentPurchase.updates —— 旧的 Purchase.updates 发出的是需要显式确认的 PurchaseIntent 对象。购买结果现在直接由 product.purchase()VerificationResult<Transaction> 的形式返回。
  • Product 属性重命名 —— skuidtitledisplayNamepriceLabeldisplayPrice
  • Product.products(domain:for:) 已简化 —— 移除了 domain 参数,SDK 会自动读取 bundle identifier。
  • 购买错误改为抛出 —— 请将 .failed(let error) 结果情形替换为围绕 product.purchase()do/catch
  • transaction.finish() 不再抛出异常 —— 请移除 try 关键字。

破坏性变更一览

旧 API新 API
product.skuproduct.id
product.titleproduct.displayName
product.priceLabelproduct.displayPrice
product.priceValueproduct.displayPrice(已格式化)或 product.priceDecimal
Product.products(domain:for:)Product.products(for:) —— 已移除 domain
PurchaseResult.failed(let error)throws AppCoinsSDKError —— 使用 do/catch
try await purchase.finish()await transaction.finish() —— 不再抛出异常
Purchase.unfinished()(async,throws)Transaction.unfinished —— AsyncStream,不抛异常
Purchase.all()(async,throws)Transaction.all —— AsyncStream
Purchase.latest(sku:)Transaction.latest(for: productID:)
Purchase.updatesPurchaseIntent 流)已移除 —— 改用 product.purchase() 的返回值
PurchaseIntent.confirm()已移除
PurchaseIntent.reject()已移除

Xcode 配置

请确认您的 Xcode target 已完成以下设置。这些是 SDK 正常运行的必要条件:

  1. Keychain Sharing

    1. Project Navigator(左侧栏)中选择您的工程。
    2. TARGETS 下选择您的 target。
    3. 进入 Signing & Capabilities 标签页。
    4. 点击 + 按钮添加新能力。
    5. 搜索 Keychain Sharing 并选中。
    6. Keychain Groups 字段中,将默认值准确替换为 com.aptoide.appcoins-wallet
  2. URL Scheme

    1. TARGETS 下选择您的 target。
    2. 进入 Info 标签页。
    3. 展开 URL Types 部分并点击 +
    4. URL Schemes 设置为 $(PRODUCT_BUNDLE_IDENTIFIER).iapRole 设置为 Editor
  3. MKSellsDigitalGoods

    1. Info 标签页中滚动到 Custom iOS Target Properties 部分并点击 +
    2. 添加键 MKSellsDigitalGoods,并将其值设置为 YES(Boolean)。

1. 更新购买流程

变更前:

let result = await product.purchase()

switch result {
case .success(let verificationResult):
switch verificationResult {
case .verified(let purchase):
try await purchase.finish()
case .unverified(let purchase, let error):
print("Unverified: \(error)")
}
case .pending:
break
case .userCancelled:
break
case .failed(let error):
print("Purchase failed: \(error)")
}

变更后:

do {
let result = try await product.purchase()

switch result {
case .success(let verificationResult):
switch verificationResult {
case .verified(let transaction):
await transaction.finish()
case .unverified(let transaction, let error):
print("Unverified: \(error)")
}
case .pending:
break
case .userCancelled:
break
}
} catch let error as AppCoinsSDKError {
print("Purchase failed: \(error)")
}

两处差异是:

  1. product.purchase() 现在写作 try await product.purchase()——该调用可能抛出异常。
  2. try await purchase.finish() 现在写作 await transaction.finish()——该调用不再抛出异常。

2. 更新未完成交易的处理

变更前:

func processUnfinished() async {
do {
let purchases = try await Purchase.unfinished()
for purchase in purchases {
giveItem(for: purchase.sku)
try await purchase.finish()
}
} catch {
print("Error loading unfinished: \(error)")
}
}

变更后:

func processUnfinished() async {
if await AppcSDK.isAvailable() {
for await verificationResult in Transaction.unfinished {
if case .verified(let transaction) = verificationResult {
giveItem(for: transaction.productID)
await transaction.finish()
}
}
}
}

Transaction.unfinished 是一个 AsyncStream——请使用 for await 遍历。既不需要 try,也不存在需要单独遍历的包装数组。

⚠️
请在每次应用启动时调用此逻辑。在此前会话中已付款的用户,只有在其交易被完成后才能收到商品。购买若未被消耗,将在 24 小时后自动退款。

3. 更新 Transaction.allTransaction.latest

变更前:

let purchases = try await Purchase.all()
let latest = try await Purchase.latest(sku: "gas")

变更后:

// Iterate all transactions
for await verificationResult in Transaction.all {
if case .verified(let transaction) = verificationResult {
// handle transaction
}
}

// Get the latest transaction for a product
if let verificationResult = await Transaction.latest(for: "gas") {
if case .verified(let transaction) = verificationResult {
// handle transaction
}
}

4. 移除 PurchaseIntent 的处理逻辑

旧版 SDK 通过 Purchase.updates 暴露了一个 PurchaseIntent 对象流,需要显式确认或拒绝。请删除所有订阅 Purchase.updates 的代码——没有替代的流。购买结果由 product.purchase() 直接返回。

变更前:

for await intent in Purchase.updates {
if User.isSignedIn {
let result = await intent.confirm()
// handle result
}
}

变更后: 完全删除该订阅逻辑,改在调用 product.purchase() 的位置处理结果:

let result = try await product.purchase()
switch result {
case .success(let verificationResult):
if case .verified(let transaction) = verificationResult {
giveItem(for: transaction.productID)
await transaction.finish()
}
case .pending, .userCancelled:
break
}

如果您此前依赖「等用户完成认证后再确认」的机制,请先保存待购买的商品,待用户登录后再调用 product.purchase()

5. 错误处理的变化

变更前:

switch result {
case .failed(let error):
switch error {
case .networkError:
showNetworkAlert()
case .purchaseNotAllowed:
showNotAllowedAlert()
default:
showGenericAlert()
}
// ...
}

变更后:

do {
let result = try await product.purchase()
// handle result
} catch let error as AppCoinsSDKError {
switch error {
case .networkError:
showNetworkAlert()
case .purchaseNotAllowed:
showNotAllowedAlert()
case .productUnavailable:
showUnavailableAlert()
case .notEntitled:
showEntitlementAlert()
case .systemError:
showSystemErrorAlert()
case .unknown:
showGenericAlert()
}
}

所有 AppCoinsSDKError 情形均保持不变,改变的只是接收方式——从结果情形改为抛出的异常。