0

My restore purchases seems to work correctly, except it crashes the app when you press it a second time. Is there a work around for this? Here is my code:

import UIKit
import StoreKit

class SettingsVC: UIViewController, SKPaymentTransactionObserver {

@IBOutlet weak var restorePurchaseButton: UIButton!
@IBOutlet weak var privacyPolicyButton: UIButton!
@IBOutlet weak var termsofUseButton: UIButton!
@IBOutlet weak var unlockButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    SKPaymentQueue.default().add(self)
    checkUnlockButton()


}

@IBAction func RestoreBtnClicked(_ sender: Any) {

    SKPaymentQueue.default().restoreCompletedTransactions()
    restorePurchaseButton.isEnabled = false
}


func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions{
       if transaction.transactionState == .restored{
            UserDefaults.standard.set(true, forKey: "payment")
            SKPaymentQueue.default().finishTransaction(transaction)

       }
    }
}

Edit: Here is the second view controller. It contains the button that allows the user to purchase the product. The restore purchase button is in a separate view controller. I am relatively new to Swift so please try to dumb down your answer for me haha.

import Foundation
import UIKit
import StoreKit

class UnlockContentVC: UIViewController, SKPaymentTransactionObserver {

@IBOutlet weak var removeAds: UIButton!
@IBOutlet weak var noThanks: UIButton!

override func viewDidLoad() {
    //removeAds.layer.cornerRadius = 10

    SKPaymentQueue.default().add(self)

    removeAds.layer.cornerRadius = removeAds.frame.height / 2

}



@IBAction func dismissModal(_ sender: Any) {

    navigationController?.popViewController(animated: true)

    dismiss(animated: true, completion: nil)

}

@IBAction func OnClickUlockBtn(_ sender: Any) {
    if SKPaymentQueue.canMakePayments(){
        //can make payments
        let paymentRequest = SKMutablePayment()
        paymentRequest.productIdentifier = productID
        SKPaymentQueue.default().add(paymentRequest)

    }else{
        //can't make payments
        print("Device can not make payments...")
    }

}


func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions{
        if transaction.transactionState == SKPaymentTransactionState.purchased{
            //User Payment Successfull
            UserDefaults.standard.set(true, forKey: "payment")
            print("payment was successfull")
            SKPaymentQueue.default().finishTransaction(transaction)
        }else if transaction.transactionState == .failed{
            //User Payment failed
            print("transaction failed...")
            SKPaymentQueue.default().finishTransaction(transaction)
            if let error = transaction.error {
                print("\(error.localizedDescription)")
            }
        } 
    }
}

}
belgrim
  • 123
  • 9
  • 1
    What is the log saying when the crash occurs? – Chris Feb 06 '19 at 21:13
  • @Chris It's in the app delegate and doesn't give me one. It just says "Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)" – belgrim Feb 06 '19 at 21:14
  • That crash typically indicates you force unwrapped a `nil`. Add an exception breakpoint to try and find out where the crash is occurring. Also, you shouldn't add the transaction observer in a view controlllers `viewDidLoad`. You need to set up a transaction observer in `didFinishLaunching` in order to process any incomplete transactions that may be pending from a previous run. – Paulw11 Feb 06 '19 at 21:23
  • You are missing quite a bit of (possibly) relevant StoreKit code. In particular, what's your code for `paymentQueueRestoreCompletedTransactionsFinished`? Please, post the full SK code, maybe I can help. –  Feb 06 '19 at 21:23
  • @dfd that method is optional, but I agree, the crash is unlikely to be in the code shown. – Paulw11 Feb 06 '19 at 21:24
  • @PaulW11, I also agree. I was editing my comment but maybe it's best in a separate one.... EDIT: It's probably best to post enough code for any of us to add it into a new project and duplicate it. I understand that it's difficult with IAPs (IDs and all) but something more would definitely be of help. And yeah, an incomplete transaction sounds like the culprit. –  Feb 06 '19 at 21:28
  • @dfd Please see my edit – belgrim Feb 06 '19 at 22:17
  • @Paulw11 Please see my edit – belgrim Feb 06 '19 at 22:17
  • 1
    You should only have one `SKPaymentTransactionObserver` and you would typically create it in your `AppDelegate`. You still need to identify the line that is actually crashing. – Paulw11 Feb 06 '19 at 22:20
  • @Paulw11 How do I identify the line? – belgrim Feb 06 '19 at 22:41
  • Set an [exception breakpoint in the debugger](https://stackoverflow.com/questions/17802662/exception-breakpoint-in-xcode); – Paulw11 Feb 06 '19 at 22:43
  • @Paulw11 I added one but nothing happened. The app just crashes and gives me error "Thread 1: EXC_BAD_ACCESS (code=1, address=0x17a10d19e2e40e42)" – belgrim Feb 06 '19 at 22:48
  • You are de-referencing an invalid pointer somewhere. It is very rare to have that crash with Swift. Normally an access exception occurs as a result of a nil optional. I suggest you set some regular breakpoint in your code and single step through until you find the line causing the crash. – Paulw11 Feb 06 '19 at 23:11

1 Answers1

1

Fixed.

override func viewDidDisappear(_ animated: Bool) {
   SKPaymentQueue.default().remove(self)
}
belgrim
  • 123
  • 9