You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

visionOS内购适配咨询:iOS16与iOS17内购代码兼容方案

问题

原本在生产环境正常运行的内购代码在visionOS中无法使用,报错信息:

'purchase(options:)' is unavailable in visionOS: Use @Environment(.purchase) to get a PurchaseAction value to call. If your app uses UIKit, use purchase(confirmIn:options:)

原有内购代码片段:

func purchase(_ product: Product) async throws -> Transaction? {
    
    let result = try await product.purchase() //TEMPORARY JAN-23
    
    switch result {
    case .success(let verification):
        // Successful purchase
            //Check whether the transaction is verified. If it isn't,
            //this function rethrows the verification error.
            let transaction = try checkVerified(verification)
            //The transaction is verified. Deliver content to the user.
            await updateCustomerProductStatus()
    // 省略其他case处理
}

尝试改用iOS 17的内购方式时,遇到版本兼容问题:直接声明@Environment(.purchase)会提示“'purchase' is only available in iOS 17.0 or newer”,给该属性添加@available(iOS 17.0, *)标注又报错“Stored properties cannot be marked potentially unavailable with '@available'”。需要修改代码以适配visionOS,同时兼容iOS 16和iOS 17版本。

解决方案

1. 条件编译拆分多版本逻辑

通过系统版本和平台的条件编译,分别实现对应环境的购买逻辑:

  • iOS 16及更早版本、非visionOS平台:保留原有的product.purchase()调用
  • iOS 17+ 和 visionOS:使用@Environment(.purchase)提供的PurchaseAction

2. 解决@Environment的版本兼容限制

由于存储属性无法标记@available,可以用计算属性+条件编译在View中获取PurchaseAction:

private var purchaseAction: PurchaseAction? {
    if #available(iOS 17.0, visionOS 1.0, *) {
        return self[Environment(\.purchase)]
    } else {
        return nil
    }
}

也可以将购买逻辑封装到独立的工具类中,通过依赖注入传递PurchaseAction,减少View内的版本判断代码。

3. 重构购买方法实现多版本兼容

完整的兼容版购买方法示例:

func purchase(_ product: Product) async throws -> Transaction? {
    if #available(iOS 17.0, visionOS 1.0, *) {
        guard let purchaseAction = purchaseAction else {
            throw PurchaseError.actionUnavailable
        }
        let verificationResult = try await purchaseAction.purchase(product)
        
        switch verificationResult {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await updateCustomerProductStatus()
            await transaction.finish()
            return transaction
        case .userCancelled, .pending:
            return nil
        }
    } else {
        let result = try await product.purchase()
        
        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await updateCustomerProductStatus()
            await transaction.finish()
            return transaction
        case .userCancelled, .pending:
            return nil
        @unknown default:
            throw PurchaseError.unknownResult
        }
    }
}

// 自定义错误类型,统一处理异常
enum PurchaseError: LocalizedError {
    case actionUnavailable
    case unknownResult
    case verificationFailed
    
    var errorDescription: String? {
        switch self {
        case .actionUnavailable: return "购买操作不可用"
        case .unknownResult: return "未知的购买结果"
        case .verificationFailed: return "交易验证失败"
        }
    }
}

4. UIKit项目适配visionOS

如果是UIKit项目,visionOS上需要调用purchase(confirmIn:options:),同样通过条件编译区分:

func purchase(_ product: Product, presentingVC: UIViewController) async throws -> Transaction? {
    if #available(visionOS 1.0, *) {
        let result = try await product.purchase(confirmIn: presentingVC)
        // 复用验证和交付逻辑
        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await updateCustomerProductStatus()
            await transaction.finish()
            return transaction
        case .userCancelled, .pending:
            return nil
        }
    } else if #available(iOS 17.0, *) {
        // iOS 17+ UIKit可使用PurchaseAction,需通过UIViewController的window获取环境
        let purchaseAction = presentingVC.view.window?.environment(\.purchase)
        guard let action = purchaseAction else {
            throw PurchaseError.actionUnavailable
        }
        let verificationResult = try await action.purchase(product)
        // 后续逻辑同前
    } else {
        // iOS 16及更早逻辑,同原有代码
        let result = try await product.purchase()
        // ...
    }
}

5. 复用通用验证逻辑

将checkVerified抽为独立方法,避免重复代码:

func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
    switch result {
    case .unverified(_, let error):
        throw error ?? PurchaseError.verificationFailed
    case .verified(let safe):
        return safe
    }
}

内容的提问来源于stack exchange,提问作者Nat Serrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 11:57:27