Ios 应用程序内的StoreKit购买似乎会在特定的iPhone型号上崩溃?
我正试图找出一个似乎发生在iPhone6上而不是iPhone7上的问题。每当我的iPhone 6用户点击按钮加载我的应用内购买数据并显示实际的应用内购买时,他们就会崩溃。对于运行iphone7s的人来说,这似乎不是问题。我不知道问题是每个设备型号的问题还是其他问题 我有一个名为Ios 应用程序内的StoreKit购买似乎会在特定的iPhone型号上崩溃?,ios,swift,sprite-kit,in-app-purchase,storekit,Ios,Swift,Sprite Kit,In App Purchase,Storekit,我正试图找出一个似乎发生在iPhone6上而不是iPhone7上的问题。每当我的iPhone 6用户点击按钮加载我的应用内购买数据并显示实际的应用内购买时,他们就会崩溃。对于运行iphone7s的人来说,这似乎不是问题。我不知道问题是每个设备型号的问题还是其他问题 我有一个名为constructPurchasePanel()的函数,它会打开一个面板,显示我的应用内购买。我的应用内购买的标题是从我存储在菜单场景中的一系列产品中获取的(这是一个单例) constructPurchasePanel()
constructPurchasePanel()
的函数,它会打开一个面板,显示我的应用内购买。我的应用内购买的标题是从我存储在菜单场景中的一系列产品中获取的(这是一个单例)
constructPurchasePanel()
code
nameLabel.text = SceneCoordinator.shared.menuScene?.products[2].localizedTitle
nameLabel2.text = SceneCoordinator.shared.menuScene?.products[1].localizedTitle
nameLabel3.text = SceneCoordinator.shared.menuScene?.products[0].localizedTitle
MenuScene产品阵列初始化
var products = [SKProduct]()
MyProducts.store.requestProducts{success, products in
if success {
self.products = products!
}
}
requestProducts()函数,以及其他与IAP相关的函数。其中99%来自Ray Wenderlich的教程:
/*
* Copyright (c) 2016 Razeware LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import StoreKit
public typealias ProductIdentifier = String
public typealias ProductsRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> ()
open class IAPHelper : NSObject {
static let IAPHelperPurchaseNotification = "IAPHelperPurchaseNotification"
fileprivate let productIdentifiers: Set<ProductIdentifier>
fileprivate var purchasedProductIdentifiers = Set<ProductIdentifier>()
fileprivate var productsRequest: SKProductsRequest?
fileprivate var productsRequestCompletionHandler: ProductsRequestCompletionHandler?
public init(productIds: Set<ProductIdentifier>) {
productIdentifiers = productIds
for productIdentifier in productIds {
let purchased = UserDefaults.standard.bool(forKey: productIdentifier)
if purchased {
purchasedProductIdentifiers.insert(productIdentifier)
print("Previously purchased: \(productIdentifier)")
} else {
print("Not purchased: \(productIdentifier)")
}
}
super.init()
SKPaymentQueue.default().add(self)
}
}
// MARK: - StoreKit API
extension IAPHelper {
public func requestProducts(completionHandler: @escaping ProductsRequestCompletionHandler) {
productsRequest?.cancel()
productsRequestCompletionHandler = completionHandler
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productsRequest!.delegate = self
productsRequest!.start()
}
public func buyProduct(_ product: SKProduct) {
print("Buying \(product.productIdentifier)...")
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
public func isProductPurchased(_ productIdentifier: ProductIdentifier) -> Bool {
return purchasedProductIdentifiers.contains(productIdentifier)
}
public class func canMakePayments() -> Bool {
return SKPaymentQueue.canMakePayments()
}
public func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
// MARK: - SKProductsRequestDelegate
extension IAPHelper: SKProductsRequestDelegate {
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let products = response.products
print("Loaded list of products...")
productsRequestCompletionHandler?(true, products)
clearRequestAndHandler()
for p in products {
print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
}
}
public func request(_ request: SKRequest, didFailWithError error: Error) {
print("Failed to load list of products.")
print("Error: \(error.localizedDescription)")
productsRequestCompletionHandler?(false, nil)
clearRequestAndHandler()
}
private func clearRequestAndHandler() {
productsRequest = nil
productsRequestCompletionHandler = nil
}
}
// MARK: - SKPaymentTransactionObserver
extension IAPHelper: SKPaymentTransactionObserver {
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch (transaction.transactionState) {
case .purchased:
complete(transaction: transaction)
break
case .failed:
fail(transaction: transaction)
break
case .restored:
restore(transaction: transaction)
break
case .deferred:
break
case .purchasing:
break
}
}
}
private func applyEffect(productIdentifier: String) {
switch(productIdentifier) {
case StackyChefProducts.smallCoinPackage:
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
Chef.sharedInstance.money += 30000
SceneCoordinator.shared.shopScene?.updateBalance()
for item in SceneCoordinator.shared.shopScene!.endlessItems { item.updateUI() }
for item in SceneCoordinator.shared.shopScene!.upgradeItems { item.updateUI() }
case StackyChefProducts.mediumCoinPackage:
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
Chef.sharedInstance.money += 90000
SceneCoordinator.shared.shopScene?.updateBalance()
for item in SceneCoordinator.shared.shopScene!.endlessItems { item.updateUI() }
for item in SceneCoordinator.shared.shopScene!.upgradeItems { item.updateUI() }
case StackyChefProducts.largeCoinPackage:
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
Chef.sharedInstance.money += 150000
SceneCoordinator.shared.shopScene?.updateBalance()
for item in SceneCoordinator.shared.shopScene!.endlessItems { item.updateUI() }
for item in SceneCoordinator.shared.shopScene!.upgradeItems { item.updateUI() }
case StackyChefProducts.removeAdsProduct:
if GameData.sharedInstance.adsOn {
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
GameData.sharedInstance.adsOn = false
MenuScene.removeAds.removeFromParent()
MenuScene.bannerView.removeFromSuperview()
}
case StackyChefProducts.couponProduct:
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
GameData.sharedInstance.coupons += 1
SceneCoordinator.shared.grabBagScene?.updateCouponButton()
SceneCoordinator.shared.grabBagScene?.updateIAPButtons()
case StackyChefProducts.couponsProduct:
SKTAudio.sharedInstance.playSoundEffect("Content/purchase.wav")
GameData.sharedInstance.coupons += 3
SceneCoordinator.shared.grabBagScene?.updateCouponButton()
SceneCoordinator.shared.grabBagScene?.updateIAPButtons()
default:
print("applyEffect() shouldn't reach this")
}
}
private func complete(transaction: SKPaymentTransaction) {
print("complete...")
deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
applyEffect(productIdentifier: transaction.payment.productIdentifier)
}
private func restore(transaction: SKPaymentTransaction) {
guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
print("restore... \(productIdentifier)")
deliverPurchaseNotificationFor(identifier: productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
applyEffect(productIdentifier: transaction.payment.productIdentifier)
}
private func fail(transaction: SKPaymentTransaction) {
print("fail...")
if let transactionError = transaction.error as NSError? {
if transactionError.code != SKErrorCode.paymentCancelled.rawValue {
print("Transaction Error: \(String(describing: transaction.error?.localizedDescription))")
}
}
SKPaymentQueue.default().finishTransaction(transaction)
}
private func deliverPurchaseNotificationFor(identifier: String?) {
guard let identifier = identifier else { return }
purchasedProductIdentifiers.insert(identifier)
UserDefaults.standard.set(true, forKey: identifier)
UserDefaults.standard.synchronize()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: IAPHelper.IAPHelperPurchaseNotification), object: identifier)
}
}
/*
*版权所有(c)2016 Razeware LLC
*
*特此向任何获得副本的人免费授予许可
*本软件和相关文档文件(“软件”)的
*在软件中不受限制,包括但不限于权利
*使用、复制、修改、合并、发布、分发、再许可和/或销售
*软件的副本,并允许向其提供软件的人员
*按照以下条件提供:
*
*上述版权声明和本许可声明应包含在
*软件的所有副本或主要部分。
*
*本软件按“原样”提供,无任何形式的明示或明示担保
*默示,包括但不限于适销性保证,
*适用于特定目的和非侵权。在任何情况下
*作者或版权持有人应承担任何索赔、损害或其他责任
*无论是在合同诉讼、侵权诉讼或其他诉讼中,由以下原因引起的责任:,
*与本软件有关或与本软件的使用或其他交易有关
*软件。
*/
进口存储套件
公共类型别名ProductIdentifier=字符串
公共类型别名ProductsRequestCompletionHandler=(\uSuccess:Bool,\uProducts:[SKProduct]?)->()
开放类IAPHelper:NSObject{
静态let IAPHelperPurchaseNotification=“IAPHelperPurchaseNotification”
fileprivate let productIdentifiers:设置
fileprivate var purchasedProductIdentifiers=Set()
fileprivate var productsRequest:SKProductsRequest?
fileprivate var productsRequestCompletionHandler:productsRequestCompletionHandler?
公共初始化(productid:Set){
productIdentifiers=ProductID
对于ProductID中的productIdentifier{
let purchased=UserDefaults.standard.bool(forKey:productIdentifier)
如果购买{
PurchasedProductIdentifier.insert(productIdentifier)
打印(“以前购买的:\(productIdentifier)”)
}否则{
打印(“未购买:\(productIdentifier)”)
}
}
super.init()
SKPaymentQueue.default().add(self)
}
}
//MARK:-StoreKit API
分机{
public func requestProducts(completionHandler:@转义产品requestcompletionhandler){
productsRequest?.cancel()
productsRequestCompletionHandler=completionHandler
productsRequest=SKProductsRequest(productIdentifiers:productIdentifiers)
productsRequest!.delegate=self
productsRequest!.start()
}
公共func buyProduct(产品:SKProduct){
打印(“购买\(product.productIdentifier)…”)
let payment=SKPayment(产品:产品)
SKPaymentQueue.default().add(付款)
}
公共函数isProductPurchased(productIdentifier:productIdentifier)->Bool{
返回PurchasedProductIdentifier.contains(productIdentifier)
}
公共类func canMakePayments()->Bool{
返回SKPaymentQueue.canMakePayments()
}
公共职能恢复采购(){
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
//标记:-SKProductsRequestDelegate
扩展IAPHelper:SKProductsRequestDelegate{
public func productsRequest(u请求:SKProductsRequest,didReceive响应:SKProductsResponse){
让产品=响应.products
打印(“已加载的产品列表…”)
productsRequestCompletionHandler?(true,产品)
clearRequestAndHandler()
产品中的p{
打印(“找到的产品:\(p.productIdentifier)\(p.localizedTitle)\(p.price.floatValue)”)
}
}
公共函数请求(请求:SKRequest,错误:error){
打印(“未能加载产品列表”)
打印(“错误:\(Error.localizedDescription)”)
productsRequestCompletionHandler?(false,nil)
clearRequestAndHandler()
}
私有函数clearRequestAndHandler(){
productsRequest=nil
productsRequestCompletionHandler=nil
}
}
//MARK:-SKPaymentTransactionObserver
扩展IAPHelper:SKPaymentTransactionObserver{
public func paymentQueue(queue:SKPaymentQueue,updatedTransactions事务:[SKPaymentTransaction]){
交易中的交易{
开关(transaction.transactionState){
案例。购买:
完成(事务:事务)
打破
案例。失败:
失败(事务:事务)
打破
案件.恢复:
还原(事务:事务)
打破
案件.推迟:
打破
案例.采购:
打破
}
}
}
私有函数applyEffect(产品标识符:字符串){
交换机(productIdentifier){
案例StackyChefProducts.smallCoinPackage:
SKTAudio.sharedInstance.playSoundEffect(“Content/purchase.wav”)
Chef.sharedInstance.money+=3