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

StoreKit 2业务模型变更功能App Store上线后失效求助

问题分析与解决方案:App Store上线后AppTransaction业务模型变更功能失效

可能的问题原因

1. 版本号字符串比较的逻辑错误

直接使用字符串比较originalMajorVersion < newBusinessModelMajorVersion存在逻辑漏洞:字符串是按字符逐个对比ASCII码,比如"10"会被判定为小于"2"(因为'1'的ASCII码小于'2'),如果生产环境中存在原始版本号为多位数的用户,会直接导致判断逻辑反转,触发功能失效。

2. 错误处理完全缺失

你的catch块是空实现,生产环境中一旦出现异常(设备未联网、App Store验证服务器故障、账号授权失败等),代码会直接跳过UserDefaults的设置步骤。而hasPurchased的默认值为false,这会直接导致符合条件的老用户无法获取权限。

3. UserDefaults初始化逻辑有漏洞

你仅在hasPurchased为false时调用检查函数,若首次调用因异常失败,后续不会再尝试重新检查,用户会一直处于未授权状态。

4. OriginalAppVersion格式异常

生产环境中originalAppVersion可能出现非预期格式:

  • 部分老用户的原始版本号包含字母(如"1.0b"),拆分后无法转成有效数字
  • 极端场景下(比如用户在应用转为免费后首次下载),originalAppVersion可能为空字符串,此时versionComponents[0]会触发数组越界,被catch捕获后同样不会设置UserDefaults。

修复方案

1. 替换字符串比较为数字比较

将版本号的主版本号转为整数后再对比,彻底避免字符串比较的逻辑错误:

func checkOriginalVersion() async {
    guard #available(iOS 16, *) else { return }
    
    do {
        let shared = try await AppTransaction.shared
        guard case .verified(let appTransaction) = shared else {
            // 验证失败,默认视为新用户
            UserDefaults.standard.set(false, forKey: "hasPurchased")
            return
        }
        
        let newBusinessModelMajorVersion = 2
        let versionComponents = appTransaction.originalAppVersion.split(separator: ".")
        
        guard let originalMajorStr = versionComponents.first, 
              let originalMajorVersion = Int(originalMajorStr) else {
            // 版本格式异常,默认视为新用户
            UserDefaults.standard.set(false, forKey: "hasPurchased")
            return
        }
        
        print("Original version: ", originalMajorVersion)
        
        let hasPurchased = originalMajorVersion < newBusinessModelMajorVersion
        UserDefaults.standard.set(hasPurchased, forKey: "hasPurchased")
    } catch {
        // 捕获所有错误,记录日志并设置默认值
        print("Failed to check original version: \(error.localizedDescription)")
        UserDefaults.standard.set(false, forKey: "hasPurchased")
    }
}

2. 完善重试与错误处理逻辑

修改viewDidAppear中的调用逻辑,增加重试机制,避免单次异常导致永久失效:

private var checkAttempts = 0
private let maxCheckAttempts = 3

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    Task {
        guard checkAttempts < maxCheckAttempts else { return }
        
        let hasPurchased = UserDefaults.standard.bool(forKey: "hasPurchased")
        if !hasPurchased {
            checkAttempts += 1
            await checkOriginalVersion()
            // 若仍未获取到结果,延迟重试
            if !UserDefaults.standard.bool(forKey: "hasPurchased") && checkAttempts < maxCheckAttempts {
                try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) // 延迟2秒
                await checkOriginalVersion()
            }
        }
    }
}

3. 增加版本格式容错处理

在解析版本号时,增加对空值、非数字格式的判断,确保任何场景下都能设置hasPurchased的值。


额外建议

  • 增加生产环境日志收集:在获取AppTransaction、版本判断等关键节点添加日志,方便后续排查问题
  • 测试边缘场景:覆盖原始版本号为多位数、带字母、空字符串的情况,确保逻辑覆盖所有可能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:55:10