PAYATHON 2026

Swift StoreKit payment queue 出现 unresolved identifier

支付小周

明确结论

问题出在枚举 case 的写法。SKPaymentTransactionState 的成员位于枚举作用域内,不能使用 SKPaymentTransactionStatePurchased、SKPaymentTransactionStateFailed 这类 Objective-C 风格的常量名。

在当前 Swift 中,应该写成:

.purchased
.failed
.restored

另外,finishTransaction 应接收已经转换为 SKPaymentTransaction 的 trans,不能传入类型仍是 AnyObject 的 transaction。

日志中的 "(Enum Value)" 常见于早期 Swift 对枚举值的默认输出,并不代表每次交易的状态都相同。排查时可以明确匹配枚举成员,或者输出 rawValue。

推荐写法

使用当前的 StoreKit 1 API 时,可以这样实现:

import StoreKit

extension YourViewController: SKPaymentTransactionObserver {
    func paymentQueue(
        _ queue: SKPaymentQueue,
        updatedTransactions transactions: [SKPaymentTransaction]
    ) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                openLevel2()
                queue.finishTransaction(transaction)

            case .failed:
                if let error = transaction.error {
                    print("Payment failed: \(error.localizedDescription)")
                }
                queue.finishTransaction(transaction)

            case .restored:
                // 恢复对应的购买内容
                openLevel2()
                queue.finishTransaction(transaction)

            case .purchasing:
                break

            case .deferred:
                break

            @unknown default:
                break
            }
        }
    }
}

代理方法已经提供了准确的参数类型,因此不需要再把数组元素从 AnyObject 转换为 SKPaymentTransaction。

如果使用的是早期 Swift 代码

题目中的方法签名和数组语法来自较早期的 Swift。当时枚举成员可能采用首字母大写的形式,但仍需通过枚举类型访问:

switch trans.transactionState {
case SKPaymentTransactionState.Purchased:
    self.openLevel2()
    SKPaymentQueue.defaultQueue().finishTransaction(trans)

case SKPaymentTransactionState.Failed:
    SKPaymentQueue.defaultQueue().finishTransaction(trans)

case SKPaymentTransactionState.Restored:
    SKPaymentQueue.defaultQueue().finishTransaction(trans)

default:
    break
}

也可以简写为:

case .Purchased:

具体大小写取决于项目使用的 Swift 和 SDK 版本。迁移到较新的 Swift 后,对应的名称是 .purchased、.failed 和 .restored。

为什么会出现 unresolved identifier

下面这个名称不是 Swift 中可以直接引用的标识符:

SKPaymentTransactionStatePurchased

这是 Objective-C API 使用的命名形式。Swift 导入该 API 后,会将这些值映射为 SKPaymentTransactionState 的枚举成员。编译器无法在当前作用域中找到 SKPaymentTransactionStatePurchased,所以会报告 unresolved identifier。

如果使用正确的枚举语法后仍然报错,还要确认代码已经导入 StoreKit:

import StoreKit

同时确认当前类型遵循:

SKPaymentTransactionObserver

关于 "(Enum Value)"

直接记录早期 Swift 枚举时,调试输出可能只有:

(Enum Value)

这只是枚举的默认字符串表现,无法据此判断具体状态。可以在 switch 的各个分支中记录状态,或者在支持 rawValue 的 SDK 中输出:

print(transaction.transactionState.rawValue)

也可以直接按状态输出更清楚的调试信息:

switch transaction.transactionState {
case .purchased:
    print("purchased")
case .failed:
    print("failed")
case .restored:
    print("restored")
case .purchasing:
    print("purchasing")
case .deferred:
    print("deferred")
@unknown default:
    print("unknown transaction state")
}

注意事项

  • .failed 和 .restored 通常也要调用 finishTransaction,否则交易可能一直留在队列中,导致重复回调。
  • .purchasing 和 .deferred 还不是最终结果,不应提前结束交易。
  • 处理 .restored 时,应先恢复用户已经购买的内容,再结束交易。
  • Swift 的 switch 默认不会贯穿到下一个分支,通常不需要在每个 case 后添加 break。
  • 支付队列观察者应在合适的生命周期中注册,并在不再使用时移除:
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().remove(self)

如果项目仍在使用题目中的旧式方法签名,应先确认实际的 Swift 和 Xcode 版本,再选择旧式的 .Purchased 或当前的 .purchased,不要混用不同时期的 API 语法。

备注:内容仅供参考。