Почему мой массив выходит за пределы?
Я пытаюсь включить покупку в приложении. По какой-то причине я получаю фатальную ошибку, в которой говорится "фатальная ошибка: индекс выходит за пределы диапазона" с массивом, содержащим продукты 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)
}
Это выполняет две вещи:
- Это делает ваш
[SKProduct]
массив необязательный - Защитное заявление безопасно разворачивает первый продукт, поэтому только если он существует, он попытается позвонить вашему
purchaseMyProduct
функция. В противном случае он просто выходит