2016-03-30 2 views
2

Я столкнулся с каким-то запутанным поведением с моей функцией восстановления покупок в приложении. В настоящее время у меня есть функция восстановления, связанная с кнопкой, она, кажется, падает, когда я активирую ее несколько раз. Например, если я ударил его, вернусь, перейдите к другому представлению, а затем верните его, чтобы снова восстановить его, и он сработает.Сбой при восстановлении покупок в swift

Может ли кто-нибудь проверить мой код и посмотреть, не хватает ли я чего-то, кто смотрит мне в лицо?

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") 
     } 
    } 
} 

}

Когда он выходит из строя, есть не так много информации, что я могу расшифровать. Я получаю сообщение об ошибке EXC_BAD_ACCESS (code = 1, address = 0xc) в моем классе AppDelegate и что-то выделенное зеленым цветом. Выведено из com.apple.root.default-qos.overcommit (Thread 4)

Любая помощь приветствуется!

+0

Что печататься в консоли? – nhgrif

+0

Это последние комментарии: «восстановление зеленого восстановление красного восстановление синего (lldb) ' – tbaldw02

ответ

3

Ваш код немного неаккуратно, позволяет пройти через это

1) Положите NSUserDefaults ключи и идентификаторы товаров в структуры выше вашего класса, чтобы вы избежать опечаток.

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) Почему вы устанавливаете запросы на ноль в методах делегатов? Это бессмысленно. Удалить все эти строки в коде

self.request = nil 

4) Вы должны также использовать originalTransaction в случае .Restore, ваш путь не совсем правильно. К сожалению, множество учебников не учат вас этому.

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) Кроме того, когда вы переход от магазина, вы должны позвонить

requests.cancel() 

, чтобы убедиться, что вы не измените ViewController в середине запроса. В моих играх spriteKit, которые заставляют меня потерпеть крах, так что хорошо, чтобы положить его туда, чтобы убедиться, что его отменили.

6) Вы называете эту линию

SKPaymentQueue.default().remove(self) 

Это должно вызываться при закрытии приложения или в вашем случае, вероятно, при выходе из магазина. Это гарантирует, что все транзакции будут удалены из наблюдателя и не появятся в будущем в виде сообщения для входа.

Сообщите мне, если это исправляет ваши сбои.

+0

Я не оригинальный плакат, а просто хотел сказать, что это сработало для меня. При попытке восстановить IAP дважды я получал сбой. Это не обычный рабочий процесс, но это крушение. Я добавил код removeTransactionObserver в функцию ViewWillDisappear моего представления, и авария исчезла. –

+0

Удивительный. Да, этот наблюдатель важен, я думаю. Если вы проверяете переполнение стека, возникает несколько вопросов, когда люди получают сбои при покупке/восстановлении. У меня была та же проблема в начале – crashoverride777

-1

Спасибо !, Работал для меня тоже !!! Об инциденте, о котором шла речь, было и для меня. Ответ выше достаточно полный, однако, только с последним комментарием он решил проблему Crash.

код я использовал в Objective-C:

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

Благодаря @ crashoverride777 (!!!) Точно такая же проблема была устранена после добавления (Swift 4):

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

рад, что это вам помогло. Счастливое кодирование – crashoverride777

Смежные вопросы