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 语法。
备注:内容仅供参考。
