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

Swift IAP Store类优化:处理SKProductsRequest返回空Products数组问题

内购SKProductsRequest空数组崩溃问题解决方案

一、哪些场景会导致SKProductsRequest返回空products数组?

以下是常见触发场景:

  • 产品配置异常:App Store Connect中内购产品ID和代码里的PRODUCT_ID不匹配,或者产品处于草稿、未审核、被拒绝状态,苹果服务器不会返回有效产品信息。
  • 网络问题:用户设备网络不稳定、请求超时,或防火墙/代理拦截了内购服务器请求,导致返回的产品列表为空。
  • 地区限制:内购产品设置了仅特定地区可用,用户当前所在地区不在允许范围内,服务器不会返回该产品。
  • 沙箱环境故障:测试时沙箱服务器临时抽风,或测试账号无对应地区权限,也可能返回空数组。
  • 重复请求冲突:多次调用getProducts()导致前一次请求响应被覆盖,处理逻辑混乱出现空数组。
  • 苹果服务器波动:内购服务偶尔出现临时故障,导致请求无法正常返回产品信息。

二、更规范的空数组处理方式

你当前的guard仅做退出处理,既无法排查问题也不能给用户有效反馈,推荐以下优化方案:

1. 处理无效产品ID

SKProductsResponse提供invalidProductIdentifiers字段,专门返回无法识别的产品ID,必须检查该字段以排查配置错误:

if !response.invalidProductIdentifiers.isEmpty {
    print("无效产品ID: \(response.invalidProductIdentifiers)")
    alertTitle = "产品加载失败"
    alertMessage = "内购产品配置异常,请联系开发者"
    presentAlert = true
    delegate?.storeUpdateReceived(store: self)
    return
}

2. 精准查找目标产品,避免下标访问

不要直接依赖数组下标list[0],通过产品ID过滤目标产品,确保即使返回多个产品也能准确获取:

// 过滤出当前需要的内购产品
list = response.products.filter { $0.productIdentifier == PRODUCT_ID }

// 检查是否找到目标产品
guard let targetProduct = list.first else {
    print("未获取到目标产品,返回的所有产品ID: \(response.products.map { $0.productIdentifier })")
    alertTitle = "加载失败"
    alertMessage = "无法获取内购产品信息,请检查网络后重试"
    presentAlert = true
    delegate?.storeUpdateReceived(store: self)
    return
}

3. 给用户明确错误提示

空数组时不能默默退出,要通过弹窗告知用户具体问题(如网络异常、产品不可用),同时提供重试选项。

4. 完善日志记录

把请求的产品ID、返回的产品列表、无效ID等信息打印或上报,方便后续排查崩溃原因,比如用户反馈问题时可通过日志定位是配置还是网络问题。

5. 优化购买逻辑,避免全局变量依赖

当前用全局变量p存储产品易出现空值或错误产品,建议直接传递产品对象:

// 修改buy方法,直接查找目标产品
func buy() {
    guard let targetProduct = list.first(where: { $0.productIdentifier == PRODUCT_ID }) else {
        alertTitle = "购买失败"
        alertMessage = "未找到可购买的产品,请重试"
        presentAlert = true
        delegate?.storeUpdateReceived(store: self)
        return
    }
    buyProduct(with: targetProduct)
}

// 修改buyProduct方法,接收产品参数
func buyProduct(with product: SKProduct) {
    let pay = SKPayment(product: product)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(pay)
}

6. 增加重试机制

给用户提供“重试”按钮,或在网络状态恢复时自动重新发起getProducts()请求,提升用户体验。

优化后的productsRequest方法示例

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    // 处理无效产品ID
    if !response.invalidProductIdentifiers.isEmpty {
        print("无效产品ID: \(response.invalidProductIdentifiers)")
        alertTitle = "产品配置错误"
        alertMessage = "无法识别内购产品,请检查配置"
        presentAlert = true
        delegate?.storeUpdateReceived(store: self)
        return
    }
    
    // 过滤出目标产品
    list = response.products.filter { $0.productIdentifier == PRODUCT_ID }
    
    guard let targetProduct = list.first else {
        print("未获取到目标产品,返回的产品列表: \(response.products.map { $0.productIdentifier })")
        alertTitle = "加载失败"
        alertMessage = "无法获取内购产品信息,请检查网络后重试"
        presentAlert = true
        delegate?.storeUpdateReceived(store: self)
        return
    }
    
    // 更新UI信息
    localTitle = targetProduct.localizedTitle
    localDescription = targetProduct.localizedDescription
    
    // 格式化价格
    let formatter = NumberFormatter()
    formatter.locale = Locale.current
    formatter.numberStyle = .currency
    if let formattedPrice = formatter.string(from: targetProduct.price){
        localPrice = "Upgrade: \(formattedPrice)"
        delegate?.storeUpdateReceived(store: self)
    } else {
        alertTitle = "价格加载失败"
        alertMessage = "无法显示产品价格,请重试"
        presentAlert = true
        delegate?.storeUpdateReceived(store: self)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:30:54