2016-03-30 8 views
2

私のアプリ内購入の復元機能と混乱しています。現在、ボタンにリンクされている復元機能があります。複数回起動するとクラッシュするようです。たとえば、私がヒットしたら、復元し、別のビューにナビゲートしてから、もう一度復元をヒットすると、クラッシュします。すぐに購入を復元するときにクラッシュする

誰かが自分のコードをチェックして、私が顔を見ているものが見つからないことがありますか?

import SpriteKit 
import StoreKit 

class PurchaseView: SKScene, SKPaymentTransactionObserver, SKProductsRequestDelegate{ 

var instructLabel = SKLabelNode() 
var priceLabel = SKLabelNode() 

var saleBadgeIcon = SKSpriteNode() 
var backIcon = SKSpriteNode() 
var restoreIcon = SKSpriteNode() 

var blueDiceDemo = SKSpriteNode() 
var redDiceDemo = SKSpriteNode() 
var greenDiceDemo = SKSpriteNode() 
var grayDiceDemo = SKSpriteNode() 

var bluePID: String = "dice.blue.add" 
var redPID: String = "dice.red.add" 
var greenPID: String = "dice.green.add" 
var grayPID: String = "dice.gray.add" 

private var request : SKProductsRequest! 
private var products : [SKProduct] = [] 

private var blueDicePurchased : Bool = false 
private var redDicePurchased : Bool = false 
private var greenDicePurchased : Bool = false 
private var grayDicePurchased : Bool = false 

override func didMoveToView(view: SKView) { 
    // In-App Purchase 
    initInAppPurchases() 

    /* 
    checkAndActivateGreenColor() 
    checkAndActivateRedColor() 
    checkAndActivateGrayColor() 
    checkAndActivateBlueColor() 
    */ 

    createInstructionLabel() 
    createBackIcon() 
    createRestoreIcon() 
    createBlueDicePurchase() 
    createRedDicePurchase() 
    createGreenDicePurchase() 
    createGrayDicePurchase() 

    checkAndActivateDiceColor(bluePID) 
    checkAndActivateDiceColor(redPID) 
    checkAndActivateDiceColor(greenPID) 
    checkAndActivateDiceColor(grayPID) 
} 

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 
    for touch in touches { 
     let location = touch.locationInNode(self) 
     let node = nodeAtPoint(location) 

     if (node == backIcon) { 
      let gameScene = GameScene(size: self.size) 
      let transition = SKTransition.doorsCloseVerticalWithDuration(0.5) 
      gameScene.scaleMode = SKSceneScaleMode.ResizeFill 
      gameScene.backgroundColor = SKColor.whiteColor() 
      self.scene!.view?.presentScene(gameScene, transition: transition) 
     } else if (node == restoreIcon) { 
      print("restore my purchases") 

      let alert = UIAlertController(title: "Restore Purchases", message: "", preferredStyle: UIAlertControllerStyle.Alert) 

      alert.addAction(UIAlertAction(title: "Restore", style: UIAlertActionStyle.Default) { _ in 
       self.restorePurchasedProducts() 
       }) 

      alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default) { _ in 

       }) 

      // Show the alert 
      self.view?.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil) 

      //restorePurchasedProducts() 
     } else if (node == blueDiceDemo) { 
      print("buy blue") 
      if (!blueDicePurchased) { 
       inAppPurchase(blueDicePurchased, pid: bluePID) 
      } 
     } else if (node == redDiceDemo) { 
      print("buy red") 
      if (!redDicePurchased) { 
       inAppPurchase(redDicePurchased, pid: redPID) 
      } 
     } else if (node == greenDiceDemo) { 
      print("buy green") 
      if (!greenDicePurchased) { 
       inAppPurchase(greenDicePurchased, pid: greenPID) 
      } 
     } else if (node == grayDiceDemo) { 
      print("buy gray") 
      if (!grayDicePurchased) { 
       inAppPurchase(grayDicePurchased, pid: grayPID) 
      } 
     } 
    } 
} 

func createBlueDicePurchase() { 
    blueDiceDemo = SKSpriteNode(imageNamed: "dice1_blue") 
    blueDiceDemo.setScale(0.6) 
    blueDiceDemo.position = CGPoint(x: CGRectGetMidX(self.frame) + blueDiceDemo.size.width * 2, y: CGRectGetMidY(self.frame)) 
    addChild(blueDiceDemo) 

    createSaleBadge(blueDiceDemo) 
} 

func createGrayDicePurchase() { 
    grayDiceDemo = SKSpriteNode(imageNamed: "dice1_gray") 
    grayDiceDemo.setScale(0.6) 
    grayDiceDemo.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)) 
    addChild(grayDiceDemo) 

    createSaleBadge(grayDiceDemo) 
} 

func createRedDicePurchase() { 
    redDiceDemo = SKSpriteNode(imageNamed: "dice1_red") 
    redDiceDemo.setScale(0.6) 
    redDiceDemo.position = CGPoint(x: CGRectGetMidX(self.frame) - blueDiceDemo.size.width * 2, y: CGRectGetMidY(self.frame)) 
    addChild(redDiceDemo) 

    createSaleBadge(redDiceDemo) 
} 

func createGreenDicePurchase() { 
    greenDiceDemo = SKSpriteNode(imageNamed: "dice1_green") 
    greenDiceDemo.setScale(0.6) 
    greenDiceDemo.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) - blueDiceDemo.size.height * 1.5) 
    addChild(greenDiceDemo) 

    createSaleBadge(greenDiceDemo) 
} 

func createInstructionLabel() { 
    instructLabel = SKLabelNode(fontNamed: "Helvetica") 
    instructLabel.text = "Click item to purchase!" 
    instructLabel.fontSize = 24 
    instructLabel.fontColor = SKColor.blackColor() 
    instructLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMaxY(self.frame) - 50) 
    addChild(instructLabel) 
} 

func createPurchasedLabel(node: SKSpriteNode) { 
    let purchasedLabel = SKLabelNode(fontNamed: "Helvetica") 
    purchasedLabel.text = "purchased" 
    purchasedLabel.fontSize = 30 
    purchasedLabel.zPosition = 2 
    purchasedLabel.fontColor = SKColor.blackColor() 
    purchasedLabel.position = CGPoint(x: 0, y: -7.5) 
    node.addChild(purchasedLabel) 
} 

func createRestoreIcon() { 
    restoreIcon = SKSpriteNode(imageNamed: "download") 
    restoreIcon.setScale(0.4) 
    restoreIcon.position = CGPoint(x: CGRectGetMinX(self.frame) + 30, y: CGRectGetMinY(self.frame) + 30) 
    addChild(restoreIcon) 
} 

func createBackIcon() { 
    backIcon = SKSpriteNode(imageNamed: "remove") 
    backIcon.setScale(0.5) 
    backIcon.position = CGPoint(x: CGRectGetMaxX(self.frame) - 30, y: CGRectGetMinY(self.frame) + 30) 
    addChild(backIcon) 
} 

func createSaleBadge(node: SKSpriteNode) { 
    saleBadgeIcon = SKSpriteNode(imageNamed: "badge") 
    saleBadgeIcon.setScale(0.4) 
    saleBadgeIcon.zPosition = 2 
    saleBadgeIcon.position = CGPoint(x: node.size.width/2, y: node.size.height/2) 
    node.addChild(saleBadgeIcon) 
} 

func inAppPurchase(dicePurchased: Bool, pid: String) { 
    let alert = UIAlertController(title: "In-App Purchases", message: "", preferredStyle: UIAlertControllerStyle.Alert) 

    // Add an alert action for each available product 
    for (var i = 0; i < products.count; i++) { 
     let currentProduct = products[i] 
     if (currentProduct.productIdentifier == pid && !dicePurchased) { 
      // Get the localized price 
      let numberFormatter = NSNumberFormatter() 
      numberFormatter.numberStyle = .CurrencyStyle 
      numberFormatter.locale = currentProduct.priceLocale 
      // Add the alert action 
      alert.addAction(UIAlertAction(title: currentProduct.localizedTitle + " " + numberFormatter.stringFromNumber(currentProduct.price)!, style: UIAlertActionStyle.Default) { _ in 
       // Perform the purchase 
       self.buyProduct(currentProduct) 
      }) 

      alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default) { _ in 

       }) 

      // Show the alert 
      self.view?.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil) 
     } 
    } 
} 

//Initializes the App Purchases 
func initInAppPurchases() { 
    SKPaymentQueue.defaultQueue().addTransactionObserver(self) 
    // Get the list of possible purchases 
    if self.request == nil { 
     self.request = SKProductsRequest(productIdentifiers: Set(["dice.green.add", "dice.blue.add", "dice.gray.add","dice.red.add"])) 
     self.request.delegate = self 
     self.request.start() 
    } 
} 

// Request a purchase 
func buyProduct(product: SKProduct) { 
    let payment = SKPayment(product: product) 
    SKPaymentQueue.defaultQueue().addPayment(payment) 
} 

// Restore purchases 
func restorePurchasedProducts() { 
    SKPaymentQueue.defaultQueue().restoreCompletedTransactions() 
} 

// StoreKit protocoll method. Called when the AppStore responds 
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) { 
    self.products = response.products 
    self.request = nil 
} 

// StoreKit protocoll method. Called when an error happens in the communication with the AppStore 
func request(request: SKRequest, didFailWithError error: NSError) { 
    print(error) 
    self.request = nil 
} 

// StoreKit protocoll method. Called after the purchase 
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { 
    for transaction in transactions { 
     switch (transaction.transactionState) { 
     case .Purchased: 
      if transaction.payment.productIdentifier == "dice.green.add" { 
       handleDiceColorPurchase(greenPID) 
       print("buying green") 
      } else if transaction.payment.productIdentifier == "dice.blue.add" { 
       handleDiceColorPurchase(bluePID) 
       print("buying blue") 
      } else if transaction.payment.productIdentifier == "dice.red.add" { 
       handleDiceColorPurchase(redPID) 
       print("buying red") 
      } else if transaction.payment.productIdentifier == "dice.gray.add" { 
       handleDiceColorPurchase(grayPID) 
       print("buying gray") 
      } else { 
       print("Error: Invalid Product ID") 
      } 
      queue.finishTransaction(transaction) 
     case .Restored: 
      if transaction.payment.productIdentifier == "dice.green.add" { 
       handleDiceColorPurchase(greenPID) 
       print("restoring green") 
      } else if transaction.payment.productIdentifier == "dice.blue.add" { 
       handleDiceColorPurchase(bluePID) 
       print("restoring blue") 
      } else if transaction.payment.productIdentifier == "dice.red.add" { 
       handleDiceColorPurchase(redPID) 
       print("restoring red") 
      } else if transaction.payment.productIdentifier == "dice.gray.add" { 
       handleDiceColorPurchase(grayPID) 
       print("restoring gray") 
      } else { 
       print("Error: Invalid Product ID") 
      } 
      queue.finishTransaction(transaction) 
     case .Failed: 
      print("Payment Error: \(transaction.error)") 
      queue.finishTransaction(transaction) 
     default: 
      print("Transaction State: \(transaction.transactionState)") 
     } 
    } 
} 

// Called after the purchase to provide the colored dice feature 
func handleDiceColorPurchase(pid: String){ 
    switch(pid) { 
     case greenPID: 
      greenDicePurchased = true 
      greenDiceDemo.alpha = 0.25 
      greenDiceDemo.removeAllChildren() 
      createPurchasedLabel(greenDiceDemo) 
     case redPID: 
      redDicePurchased = true 
      redDiceDemo.alpha = 0.25 
      redDiceDemo.removeAllChildren() 
      createPurchasedLabel(redDiceDemo) 
     case grayPID: 
      grayDicePurchased = true 
      grayDiceDemo.alpha = 0.25 
      grayDiceDemo.removeAllChildren() 
      createPurchasedLabel(grayDiceDemo) 
     case bluePID: 
      blueDicePurchased = true 
      blueDiceDemo.alpha = 0.25 
      blueDiceDemo.removeAllChildren() 
      createPurchasedLabel(blueDiceDemo) 
     default: 
      print("No action taken, incorrect PID") 
    } 

    checkAndActivateDiceColor(pid) 
    // persist the purchase locally 
    NSUserDefaults.standardUserDefaults().setBool(true, forKey: pid) 
} 

func checkAndActivateDiceColor(pid: String){ 
    if NSUserDefaults.standardUserDefaults().boolForKey(pid) { 
     switch(pid) { 
      case greenPID: 
       greenDicePurchased = true 
       greenDiceDemo.alpha = 0.25 
       greenDiceDemo.removeAllChildren() 
       createPurchasedLabel(greenDiceDemo) 
      case redPID: 
       redDicePurchased = true 
       redDiceDemo.alpha = 0.25 
       redDiceDemo.removeAllChildren() 
       createPurchasedLabel(redDiceDemo) 
      case grayPID: 
       grayDicePurchased = true 
       grayDiceDemo.alpha = 0.25 
       grayDiceDemo.removeAllChildren() 
       createPurchasedLabel(grayDiceDemo) 
      case bluePID: 
       blueDicePurchased = true 
       blueDiceDemo.alpha = 0.25 
       blueDiceDemo.removeAllChildren() 
       createPurchasedLabel(blueDiceDemo) 
      default: 
       print("No action taken, incorrect PID") 
     } 
    } 
} 

}

それがクラッシュすると、私は解読できることを多くの情報がありません。私のAppDelegateクラスと何かが緑色で強調表示されたcom.apple.root.default-qos.overcommit(スレッド4)からエンキューされたEXC_BAD_ACCESS(コード= 1、アドレス= 0xc)というエラーが表示されます

ご協力ありがとうございます!

+0

印刷した何コンソールで? – nhgrif

+0

これは最後のコメントです: '緑色の復元 赤色の復元 青色の復元 (lldb) – tbaldw02

答えて

3

あなたのコードは少し厄介である、あなたがタイプミスを避けるように、それ

1)あなたのクラスの上に構造体にあなたのNSUserDefaultsキーとプロダクトIDを入れて行くことができます。

struct ProductID { 
    static let diceGrayAdd = "dice.gray.add" 
    .... 
    } 

ので

....payment.productIdentifier == ProductID.diceGrayAdd {  

2)あなたは支払いが実際に製品を要求する前に行うことができるかどうかをチェックされていないようにそれを取得します。

guard SKPaymentQueue.canMakePayments() else { 
    // show alert that IAPs are not enabled 
    return 
} 

3)なぜ委任メソッドでリクエストをnilに設定していますか?それは意味をなさない。あなたのコード

self.request = nil 

4)また、.RestoreケースにoriginalTransactionを使うべきで、これらすべての行を削除し、あなたの方法はかなり正確ではありません。残念ながら、チュートリアルの負荷はあなたにこれを教えていません。

case .Restored: 

/// Its an optional so safely unwrap it first 
if let originalTransaction = transaction.originalTransaction {    

    if originalTransaction.payment.productIdentifier == ProductID.diceGrayAdd { 
      handleDiceColorPurchase(greenPID) 
      print("restoring green") 
     } 
     .... 
    } 

あなたは.Purchasedと.Restored例で重複したコードを記述する必要がいけないので、あなたはまた、別の関数にロック解除アクションを置くことによって、あなたのコードビットのクリーナーを作ることができます。

私の答えをチェックしてくださいこれについて最近投稿しました。 .Failedの場合もエラーを処理する必要があります。あなたは離れて店からの移行時に

Restore Purchase : Non-Consumable

5)また、あなたは、要求の途中でのViewControllerを変更しないことを確認する

requests.cancel() 

を呼び出す必要があります。私のスプライトKitのゲームでは、私はクラッシュを取得するので、そのことを確認するためにそこにそれを入れて、そのキャンセルを確認します。あなたはおそらく、あなたが店を出る際に、あなたのケースでたり、アプリを閉じたとき

6)が、あなたはこれが呼び出される必要があり、このライン

SKPaymentQueue.default().remove(self) 

を呼んでいます。これにより、すべてのトランザクションがオブザーバから削除され、将来はログインメッセージの形式で表示されなくなります。

これでクラッシュが修正された場合はお知らせください。

+0

私は元のポスターではありませんが、これは私のために働いたと言いたいだけです。 IAPを2回復元しようとするとクラッシュしました。これは通常のワークフローではありませんが、クラッシュです。私はビューのViewWillDisappear関数にremoveTransactionObserverコードを追加しました。クラッシュがなくなりました。 –

+0

うん、この観察者は私が大事に思っている。スタックのオーバーフローをチェックすると、購入/復元時にクラッシュするといういくつかの質問があります。私は最初に同じ問題を抱えていた – crashoverride777

-1

ありがとうございました! 言及されたクラッシュが私にも起こっていました。上記の答えは、しかし、クラッシュの問題を解決した最後のコメントだけで、完全に完成しました。

私はObjective-Cで使用されるコード:へ

- (void) viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:YES]; 
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; 
} 
1

感謝@ crashoverride777(!!!)全く同じ問題が(スウィフト4)追加した後に修正されました:

override func viewDidDisappear(_ animated: Bool) { 
     SKPaymentQueue.default().remove(self) 
    } 
+0

あなたを助けてくれてうれしいです。ハッピーコーディング – crashoverride777

関連する問題