在应用程序购买时更改场景崩溃应用程序

时间:2017-11-06 04:39:40

标签: ios iphone swift xcode in-app-purchase

我已经在应用程序中购买了我的游戏进入我的商店场景,并且在从商店场景转换到另一个场景时遇到了问题似乎让游戏崩溃并给了我这个错误

Thread 1: EXC_BAD_ACCESS (code=1, address=0x840f8010)

或者它给了我其他错误的多个版本,例如:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x3f8)

它有时会给我一个错误,改变子类中的行,如此

enter image description here

当我注释掉子类中搜索应用购买信息或将手机置于飞行模式的代码时,它工作正常,问题就消失了。

我有一个SKnode的子类,它获取有关可购买物品的信息,然后通过使用SKLabels和精灵节点显示它,以便在商店中显示购买图片,如下所示:

class InAppPurchaseItems: SKNode, SKProductsRequestDelegate {

var shopItemNode = SKSpriteNode()
var itemPriceBackground = SKSpriteNode()
var shopItemLabel = SKLabelNode()
var shopItemTitleLabel = SKLabelNode()
var pressableNode = SKSpriteNode()
var itemPriceLabel = SKLabelNode()

var title: String = ""
var information: String = ""
var image: String = ""

var price:String = "X"

func createAppPurchaseItem(ID: String, purchaseImage: String, purchaseTitle:String) {

    title = purchaseTitle
    image = purchaseImage

    createTheNode()

    //let product = SKProduct()
    let productID: NSSet = NSSet(objects: ID)  //"RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
    let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
    request.delegate = self as? SKProductsRequestDelegate
    request.start()
}

public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    print("product request...")
    let myProduct = response.products
    for product in myProduct {
        print("product added")

        if product.productIdentifier == "RedShield.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            information = product.localizedDescription

        } else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            information = product.localizedDescription

        } else if product.productIdentifier == "1500Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirOne")

        } else if product.productIdentifier == "7500Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirTwo")

        } else if product.productIdentifier == "14000Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirThree")

        } else if product.productIdentifier == "28000Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFour")

        } else if product.productIdentifier == "65000Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFive")

        } else if product.productIdentifier == "128000Stars.Astrum.Purchase" {

            price = priceStringForProduct(item: product)!
            shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirSix")

        }

         createShopLabels()
    }
}

func priceStringForProduct(item: SKProduct) -> String? {
    let price = item.price
    if price == 0 {
        return "GET" //or whatever you like
    } else {
        let numberFormatter = NumberFormatter()
        let locale = item.priceLocale
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = locale
        return numberFormatter.string(from: price)
    }
}

func createTheNode() {

    let tex:SKTexture = SKTexture(imageNamed: image)

    shopItemNode = SKSpriteNode(texture: tex, color: SKColor.black, size: CGSize(width: 85, height: 85)) //frame.maxX / 20, height: frame.maxY / 20))
    shopItemNode.zPosition = -10
    shopItemNode.position = CGPoint(x: 0, y: 35)

    self.addChild(shopItemNode)
    self.name = "ShopItem"
    self.zPosition = -11

    shopItemTitleLabel = SKLabelNode(fontNamed: "Avenir-Black")
    shopItemTitleLabel.fontColor = UIColor.black;
    shopItemTitleLabel.fontSize = 15 //self.frame.maxY/30
    shopItemTitleLabel.position = CGPoint (x: 0, y: -30)
    shopItemTitleLabel.text = "\(title)"
    shopItemTitleLabel.zPosition = -9
    self.addChild(shopItemTitleLabel)

    itemPriceBackground = SKSpriteNode(texture: SKTexture(imageNamed: "PriceShopBackground"), color: .clear, size: CGSize(width: 80, height: 30)) //SKSpriteNode(color: SKColor.black, size: CGSize(width: 65, height: 20))
    //itemPriceBackground.alpha = 0.4
    itemPriceBackground.zPosition = -10
    itemPriceBackground.position = CGPoint(x: 0, y: -54)
    addChild(itemPriceBackground)

    pressableNode = SKSpriteNode(texture: nil, color: .clear, size: CGSize(width: 100, height: 140))
    pressableNode.zPosition = -7
    pressableNode.position = CGPoint(x: 0, y: 0)
    shopItemSprites.append(pressableNode)
    addChild(pressableNode)

}

func createShopLabels() {

    shopItemLabel = SKLabelNode(fontNamed: "Avenir-Black")
    shopItemLabel.fontColor = UIColor.white;
    shopItemLabel.fontSize = 15 //self.frame.maxY/30
    shopItemLabel.position = CGPoint (x: 0, y: -60)
    shopItemLabel.text = "\(price)"
    shopItemLabel.zPosition = -9
    addChild(shopItemLabel)

 }

}

然后使用以下代码在商店场景中显示它们:

 let ShopItem = InAppPurchaseItems()
 ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase", purchaseImage: "2StarCoin", purchaseTitle: "+2 In Game Pickups")
 ShopItem.position = CGPoint(x: self.frame.midX / 1.6, y: self.frame.midY * 0.8)
 ShopItem.zPosition = 100
 ShopItem.name = "Shp0"
 moveableArea.addChild(ShopItem)

购物主要类

商店主类也有应用程序购买代码,用于购买产品,也能够像子类中一样搜索产品信息,如下所示

class ShopItemMenu: SKScene, SKProductsRequestDelegate, SKPaymentTransactionObserver {

//Purchase Variables
var listOfProducts = [SKProduct]()
var p = SKProduct()

override func didMoveToView(to view: SKView) {

 let ShopItem = InAppPurchaseItems()
 ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase", purchaseImage: "2StarCoin", purchaseTitle: "+2 In Game Pickups")
 ShopItem.position = CGPoint(x: self.frame.midX / 1.6, y: self.frame.midY * 0.8)
 ShopItem.zPosition = 100
 ShopItem.name = "Shp0"
 moveableArea.addChild(ShopItem)

}


//This function allows for a product to be bought buy the user and starts the proccess for purchasing
func appPurchaseBuying(appPurchaseID:String) {
    for product in listOfProducts {
        let prodID = product.productIdentifier
        if(prodID == appPurchaseID) {
            p = product
            buyProduct()
        }
    }
}

//This Function restores all previously purchased Items (use this for the restor button.
func restorePurchasesOfItems() {
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().restoreCompletedTransactions()
}


//This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts)
func checkCanMakePayment() {
    if (SKPaymentQueue.canMakePayments()) {
        print("can make payments...")
        let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
        let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
        request.delegate = self
        request.start()
    } else {
        let alert = UIAlertController(title: "In-App Purchases Not Enabled", message: "Please enable In App Purchases in Settings", preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)

            let url: NSURL? = NSURL(string: UIApplicationOpenSettingsURLString)
            if url != nil
            {
                UIApplication.shared.openURL(url! as URL)
            }

        }))
        alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)
        }))

        if let vc = self.scene?.view?.window?.rootViewController {
            vc.present(alert, animated: true, completion: nil)
        }
    }
}

//This allows the user to buy the product with a product idetifier given by the variable "p"
func buyProduct() {
    print("buying " + p.productIdentifier)
    let pay = SKPayment(product: p)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(pay as SKPayment)
}

//This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
   /* print("product request...")
    let myProduct = response.products
    for product in myProduct {
        print("product added")

        if product.productIdentifier == "RedShield.Astrum.Purchase" {

            shieldPurchasePrice = priceStringForProduct(item: product)!

        } else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" {

            DoubleCoinPurchasePrice = priceStringForProduct(item: product)!
        }

      /*print(product.productIdentifier)
        print(product.localizedTitle)
        print(product.localizedDescription)
        print(product.price)
     */
        listOfProducts.append(product)
    }*/
}




func priceStringForProduct(item: SKProduct) -> String? {
    let price = item.price
    if price == 0 {
        return "GET" //or whatever you like
    } else {
        let numberFormatter = NumberFormatter()
        let locale = item.priceLocale
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = locale
        return numberFormatter.string(from: price)
    }
}



    //This Function restores all the already purchased products so that things can be restored such as shield
    public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    print("restoring all...")
    for transaction in queue.transactions {
        let t: SKPaymentTransaction = transaction
        let prodID = t.payment.productIdentifier as String

        switch prodID {
        case "RedShield.Astrum.Purchase":
            isRedShieldPurchaseOn = true

            let defaults = UserDefaults.standard
            defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
            print("finished restoring this purchase")

        case "DoubleCoin.Astrum.Purchase":
            isCoinPurchaseOn = true

            let defaults = UserDefaults.standard
            defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
            print("finished restoring this purchase")

        default:
            print("IAP not found")
        }
    }

    alert(title: "Restored", msg: "Purchases were restored")
}

//This Function is run when the user makes a purchase and checks the state of the purchase to make sure it works
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    print("adding payment...")

    for transaction: AnyObject in transactions {
        let trans = transaction as! SKPaymentTransaction
        print(trans.error)

        switch trans.transactionState {
        case .purchased:
            print("buying ok, Unlocking purchase...")
            print(p.productIdentifier)

            let prodID = p.productIdentifier
            switch prodID {

                case "RedShield.Astrum.Purchase":
                    isRedShieldPurchaseOn = true

                    let defaults = UserDefaults.standard
                    defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
                    print("unlocked Purchase")

                case "DoubleCoin.Astrum.Purchase":
                    isCoinPurchaseOn = true

                    let defaults = UserDefaults.standard
                    defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
                    print("unlocked Purchase")

                case "SOME IN APP PURCHASE ID HERE":
                    print("unlocked Purchase")

                default:
                    print("IAP Not found")
            }

            queue.finishTransaction(trans)

        case .failed:
            print("error with payment...")
            queue.finishTransaction(trans)

        default:
            print("Default")
        }
    }
}

我是以正确的方式做这件事,还是有更好的方法来解决我遇到的崩溃问题?

修改

enter image description here

编辑2

enter image description here

编辑3

enter image description here

编辑4

import Foundation
import SpriteKit
import StoreKit

class PurchaseService {

static let session = PurchaseService()
var products = [SKProduct]()
var p = SKProduct()



//This function allows for a product to be bought buy the user and starts the proccess for purchasing
func appPurchaseBuying(appPurchaseID:String) {
    for product in products {
        let prodID = product.productIdentifier
        if(prodID == appPurchaseID) {
            p = product
            buyProduct()
        }
    }
}

//This Function restores all previously purchased Items (use this for the restor button.
func restorePurchasesOfItems() {
    //SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().restoreCompletedTransactions()
}


//This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts)
func checkCanMakePayment() {
    if (SKPaymentQueue.canMakePayments()) {
        print("can make payments...")
        let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
        let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
        //request.delegate = self
        request.start()
    } else {
        let alert = UIAlertController(title: "In-App Purchases Not Enabled", message: "Please enable In App Purchases in Settings", preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)

            let url: NSURL? = NSURL(string: UIApplicationOpenSettingsURLString)
            if url != nil
            {
                UIApplication.shared.openURL(url! as URL)
            }

        }))
        alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)
        }))
    }
}

//This allows the user to buy the product with a product idetifier given by the variable "p"
func buyProduct() {
    print("buying " + p.productIdentifier)
    let pay = SKPayment(product: p)
    //SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(pay as SKPayment)
}

//This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    print("product request...")
    let myProduct = response.products
    for product in myProduct {
        print("product added")

        products.append(product)
    }
}




func priceStringForProduct(item: SKProduct) -> String? {
    let price = item.price
    if price == 0 {
        return "GET" //or whatever you like
    } else {
        let numberFormatter = NumberFormatter()
        let locale = item.priceLocale
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = locale
        return numberFormatter.string(from: price)
    }
}



//This Function restores all the already purchased products so that things can be restored such as shield
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    print("restoring all...")
    for transaction in queue.transactions {
        let t: SKPaymentTransaction = transaction
        let prodID = t.payment.productIdentifier as String

        switch prodID {
        case "RedShield.Astrum.Purchase":
            isRedShieldPurchaseOn = true

            let defaults = UserDefaults.standard
            defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
            print("finished restoring this purchase")

        case "DoubleCoin.Astrum.Purchase":
            isCoinPurchaseOn = true

            let defaults = UserDefaults.standard
            defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
            print("finished restoring this purchase")

        default:
            print("IAP not found")
        }
    }

    //alert(title: "Restored", msg: "Purchases were restored")
}

//This Function is run when the user makes a purchase and checks the state of the purchase to make sure it works
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    print("adding payment...")

    for transaction: AnyObject in transactions {
        let trans = transaction as! SKPaymentTransaction
        print(trans.error)

        switch trans.transactionState {
        case .purchased:
            print("buying ok, Unlocking purchase...")
            print(p.productIdentifier)

            let prodID = p.productIdentifier
            switch prodID {

            case "RedShield.Astrum.Purchase":
                isRedShieldPurchaseOn = true

                let defaults = UserDefaults.standard
                defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
                print("unlocked Purchase")

            case "DoubleCoin.Astrum.Purchase":
                isCoinPurchaseOn = true

                let defaults = UserDefaults.standard
                defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
                print("unlocked Purchase")

            case "SOME IN APP PURCHASE ID HERE":
                print("unlocked Purchase")

            default:
                print("IAP Not found")
            }

            queue.finishTransaction(trans)

        case .failed:
            print("error with payment...")
            queue.finishTransaction(trans)

        default:
            print("Default")
        }
    }
}

}

2 个答案:

答案 0 :(得分:2)

在游戏场景中包含所有StoreKit代码会使您更难以隔离您所拥有的问题。我建议你制作一个新的swift文件,让我们用这样的静态实例称之为PurchaseService:

class PurchaseService {
      static let session = PurchaseService()
      var products = [SKProduct]()
      // code
}

您可以在此处实施所有与采购相关的逻辑。我通常使用getPurchases函数从商店加载可用的购买并从AppDelegate.swift文件的应用程序函数调用它。这样可以确保您的购买很早就开始加载,并在您需要的第一时间准备好(因为您创建了一个静态实例,您可以在需要通过PurchaseService.session进行购买时参考它...)

要获得价格,您可以使用迭代产品变量的函数并检查产品ID:

func price(for productID:String)->Double{
  if products.count>0 {
  for product in products {
    if product.productIdentifier == productID {
      return product.price.doubleValue
    }
  }
 }
}

如果您遵守SKProductRequestDelegate协议,则无需有条件地自我投射:

 // unnecessary:  request.delegate = self as? SKProductsRequestDelegate
    request.delegate = self

想知道您是否公开了productRequest方法,因为在请求返回SKProductResponse对象时,self不再可用。

关于项目中的Objective-C代码:我发现你可能正在使用Firebase(从你的控制台消息我推断)并且它有一些客观的c片段。

答案 1 :(得分:1)

在黑暗中捅了一下,但是你的图像名称拼写错误q)a 123 134 256 345 789 123 256 345 678 789 q)count each group raze{x comb[count x;2]}'[a] 123 134| 1 123 256| 2 134 256| 1 ... 345 789| 2 ... ......因为这些名字不存在图像,所以它有可能崩溃吗?

即。这一行:Tier

应该更改为:shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirSix")

如果这不是您的问题,我建议您修改资产名称,名称中的拼写错误会让您感到困惑。