Почему мой массив выходит за пределы?

Я пытаюсь включить покупку в приложении. По какой-то причине я получаю фатальную ошибку, в которой говорится "фатальная ошибка: индекс выходит за пределы диапазона" с массивом, содержащим продукты SKProduct. У меня есть один расходный материал при покупке приложения, но я могу решить добавить его позже. Я пытаюсь получить доступ к продукту PREMIUM_PRODUCT_ID, чтобы я мог сделать покупку. Ошибка возникает при вызове функции purchaseMyProduct. Есть идеи, почему массив iapProducts выходит за пределы? Спасибо за вашу помощь!
В этой строке происходит фатальная ошибка purchaseMyProduct(product: iapProducts[0])

import UIKit
import StoreKit


class Settings: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {

    let PREMIUM_PRODUCT_ID = "---------------"

    var productID = ""
    var productsRequest = SKProductsRequest()
    var iapProducts = [SKProduct]()
    var nonConsumablePurchaseMade = UserDefaults.standard.bool(forKey: "nonConsumablePurchaseMade")



    @IBOutlet weak var adsBtn: UIButton!
    @IBAction func restorePurchase(_ sender: Any) {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()

        // Check your In-App Purchases
        print("NON CONSUMABLE PURCHASE MADE: \(nonConsumablePurchaseMade)")


        // Fetch IAP Products available
        fetchAvailableProducts()

        UIAlertView(title: "IAP Tutorial",
                    message: "You've successfully restored your purchase!",
                    delegate: nil, cancelButtonTitle: "OK").show()
    }

    @IBAction func review(_ sender: Any) {
        UIApplication.shared.openURL(NSURL(string: "-----------------")! as URL)

    }


    @IBAction func removeAds(_ sender: Any) {
        //UIApplication.shared.openURL(NSURL(string: "----------------")! as URL)

        purchaseMyProduct(product: iapProducts[0])

    }


    override func viewDidLoad() {
        super.viewDidLoad()


        // Do any additional setup after loading the view.


    }

   ...

    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        nonConsumablePurchaseMade = true
        UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")

        /*UIAlertView(title: "IAP Tutorial",
                    message: "You've successfully restored your purchase!",
                    delegate: nil, cancelButtonTitle: "OK").show()*/
    }

    func fetchAvailableProducts()  {

        // Put here your IAP Products ID's
        let productIdentifiers = NSSet(objects:
                                       PREMIUM_PRODUCT_ID
        )

        productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
        productsRequest.delegate = self
        productsRequest.start()
    }

    func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
        if (response.products.count > 0) {
            iapProducts = response.products

            // 1st IAP Product (Consumable) ------------------------------------
            let firstProduct = response.products[0] as SKProduct

            // Get its price from iTunes Connect
            let numberFormatter = NumberFormatter()
            numberFormatter.formatterBehavior = .behavior10_4
            numberFormatter.numberStyle = .currency
            numberFormatter.locale = firstProduct.priceLocale
            let price1Str = numberFormatter.string(from: firstProduct.price)

            // Show its description
            //consumableLabel.text = firstProduct.localizedDescription + "\nfor just \(price1Str!)"
            // ------------------------------------------------



            // 2nd IAP Product (Non-Consumable) ------------------------------
            let secondProd = response.products[0] as SKProduct

            // Get its price from iTunes Connect
            numberFormatter.locale = secondProd.priceLocale
            let price2Str = numberFormatter.string(from: secondProd.price)

            // Show its description
            //nonConsumableLabel.text = secondProd.localizedDescription + "\nfor just \(price2Str!)"
            // ------------------------------------
        }
    }

    func canMakePurchases() -> Bool {  return SKPaymentQueue.canMakePayments()  }
    func purchaseMyProduct(product: SKProduct) {
        if self.canMakePurchases() {
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(self)
            SKPaymentQueue.default().add(payment)

            print("PRODUCT TO PURCHASE: \(product.productIdentifier)")
            productID = product.productIdentifier


            // IAP Purchases dsabled on the Device
        } else {
            UIAlertView(title: "IAP Tutorial",
                        message: "Purchases are disabled in your device!",
                        delegate: nil, cancelButtonTitle: "OK").show()
        }
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction:AnyObject in transactions {
            if let trans = transaction as? SKPaymentTransaction {
                switch trans.transactionState {

                case .purchased:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)

                    // The Consumable product (10 coins) has been purchased -> gain 10 extra coins!
                    if productID == PREMIUM_PRODUCT_ID {

                        // Save your purchase locally (needed only for Non-Consumable IAP)
                        nonConsumablePurchaseMade = true
                        UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")

                        //premiumLabel.text = "Premium version PURCHASED!"

                        UIAlertView(title: "IAP Tutorial",
                                    message: "You've successfully unlocked the Premium version!",
                                    delegate: nil,
                                    cancelButtonTitle: "OK").show()
                    }

                    break

                case .failed:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    break
                case .restored:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    break

                default: break
                }}}
    }

}

1 ответ

Решение

Ваш код слишком длинный, поэтому я позволил себе сделать обоснованное предположение, что имеет значение:

import UIKit
import StoreKit

class Settings: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {

    var iapProducts = [SKProduct]()

    @IBAction func removeAds(_ sender: Any) {
        purchaseMyProduct(product: iapProducts[0])

    }
}

Когда вы объявляете iapProducts, вы создаете пустой массив, который не содержит значений.

Тогда в вашем removeAds функция, вы говорите ей сделать что-то с первым элементом.

Я собираюсь сделать дикое предположение, что ничего не существует, поэтому вы заставляете его обращаться к объекту, который не существует.

Попробуйте следующее и посмотрите, исправит ли это вашу проблему.

var iapProducts: [SKProduct]?

@IBAction func removeAds(_ sender: Any) {
    guard let product = iapProducts?.first else { return }

    purchaseMyProduct(product: product)
}

Это выполняет две вещи:

  1. Это делает ваш [SKProduct] массив необязательный
  2. Защитное заявление безопасно разворачивает первый продукт, поэтому только если он существует, он попытается позвонить вашему purchaseMyProduct функция. В противном случае он просто выходит
Другие вопросы по тегам