Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SKProductsRequest returning 0 Products

I'm trying to do IAP however for some reason, my SKProductsRequest returns 0 products.

-Test products have been added to iTunes connect properly

-Banking and Taxing information is filled

-The product's bundle id matches the app bundle id

-I've waited up to two days for it to get processed through the servers

I used this youtube tutorial to build the app: https://www.youtube.com/watch?v=zRrs7O5yjKI

And here is the code:

import UIKit
import StoreKit

class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

    @IBOutlet weak var lblAd: UILabel!
    @IBOutlet weak var lblCoinAmount: UILabel!

    @IBOutlet weak var outRemoveAds: UIButton!
    @IBOutlet weak var outAddCoins: UIButton!
    @IBOutlet weak var outRestorePurchases: UIButton!



    var coins = 50

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        outRemoveAds.isEnabled = false
        outAddCoins.isEnabled = false
        outRestorePurchases.isEnabled = false

        if(SKPaymentQueue.canMakePayments()) {
            print("IAP is enabled, loading")
            let productID: NSSet = NSSet(objects: "com.IAPTesters.10Dolla", "com.IAPTesters.RemoveAds")
            let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
            request.delegate = self
            request.start()
        } else {
            print("please enable IAPS")
        }
    }
    @IBAction func btnRemoveAds(_ sender: Any) {
        print("rem ads")
        for product in list {
            let prodID = product.productIdentifier
            if(prodID == "com.IAPTesters.RemoveAds") {
                p = product
                buyProduct()
            }
        }
    }

    @IBAction func btnAddCoins(_ sender: Any) {
        for product in list {
            let prodID = product.productIdentifier
            if(prodID == "com.IAPTesters.10Dolla") {
                p = product
                buyProduct()
            }
        }
    }

    @IBAction func btnRestorePurchases(_ sender: Any) {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    func buyProduct() {
        print("buy " + p.productIdentifier)
        let pay = SKPayment(product: p)
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().add(pay as SKPayment)
    }

    func removeAds() {
        lblAd.removeFromSuperview()
    }

    func addCoins() {
        coins += 50
        lblCoinAmount.text = "\(coins)"
    }

    var list = [SKProduct]()
    var p = SKProduct()

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("product request")
        let myProduct = response.products
        for product in myProduct {
            print("product added")
            print(product.productIdentifier)
            print(product.localizedTitle)
            print(product.localizedDescription)
            print(product.price)

            list.append(product)
        }

        outRemoveAds.isEnabled = true
        outAddCoins.isEnabled = true
        outRestorePurchases.isEnabled = true
    }

    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        print("transactions restored")
        for transaction in queue.transactions {
            let t: SKPaymentTransaction = transaction
            let prodID = t.payment.productIdentifier as String

            switch prodID {
            case "com.IAPTesters.RemoveAds":
                print("remove ads")
                removeAds()
            case "com.IAPTesters.10Dolla":
                print("add coins to account")
                addCoins()
            default:
                print("IAP not found")
            }
        }
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        print("add payment")

        for transaction: AnyObject in transactions {
            let trans = transaction as! SKPaymentTransaction
            print(trans.error)
            switch trans.transactionState {
            case .purchased:
                print("buy ok, unlock IAP HERE")
                print(p.productIdentifier)

                let prodID = p.productIdentifier
                switch prodID {
                case "com.IAPTesters.RemoveAds":
                    print("remove ads")
                    removeAds()
                case "com.IAPTesters.10Dolla":
                    print("add coins to account")
                    addCoins()
                default:
                    print("IAP not found")
                }
                queue.finishTransaction(trans)
            case .failed:
                print("buy error")
                queue.finishTransaction(trans)
                break
            default:
                print("Default")
                break
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

When you run the app it will print "IAP is enabled, loading" and than "product request" but nothing else.

If I print response.invalidProductIdentifiers in the productsRequest function it will return my products: ["com.IAPTesters.RemoveAds", "com.IAPTesters.10Dolla"]

Thanks in advance for the help

like image 999
tee Avatar asked Jan 03 '18 02:01

tee


2 Answers

It turns out that my banking and taxing information was filled out incorrectly. I refilled it and than I had to wait about 30 minutes for it to work again. Everything is now working correctly! Thanks for all the help

like image 80
tee Avatar answered Oct 11 '22 05:10

tee


If you refer to the SKProductsRequest documentation you will see:

Note

Be sure to keep a strong reference to the request object; otherwise, the system might deallocate the request before it can complete.

Your SKProductsRequest instance is a local constant in viewDidLoad. This will be deallocated as soon as viewDidLoad exits and since the product request will complete asynchronously, this will be before the request is completed and you will therefore never get a call back.

You should retain your SKProductsRequest in a property so that it isn't released.

class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

    @IBOutlet weak var lblAd: UILabel!
    @IBOutlet weak var lblCoinAmount: UILabel!

    @IBOutlet weak var outRemoveAds: UIButton!
    @IBOutlet weak var outAddCoins: UIButton!
    @IBOutlet weak var outRestorePurchases: UIButton!

    var productsRequest: SKProductsRequest?

    var coins = 50

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        outRemoveAds.isEnabled = false
        outAddCoins.isEnabled = false
        outRestorePurchases.isEnabled = false

        if(SKPaymentQueue.canMakePayments()) {
            print("IAP is enabled, loading")
            let productID: NSSet = NSSet(objects: "com.IAPTesters.10Dolla", "com.IAPTesters.RemoveAds")
            self.productsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
            request?.delegate = self
            request?.start()
        } else {
            print("please enable IAPS")
        }
    }

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("product request")
        let myProduct = response.products
        for product in myProduct {
            print("product added")
            print(product.productIdentifier)
            print(product.localizedTitle)
            print(product.localizedDescription)
            print(product.price)

            list.append(product)
        }

        outRemoveAds.isEnabled = true
        outAddCoins.isEnabled = true
        outRestorePurchases.isEnabled = true

        self.productsRequest = nil
    }

FYI, your implementation of paymentQueueRestoreCompletedTransactionsFinished is incorrect; you should process the restored transactions in updatedTransactions through the .restored transaction state. The paymentQueueRestoreCompletedTransactionsFinished method should only be used to update your UI or perform any other tasks that are required when the restoration process is complete.

like image 23
Paulw11 Avatar answered Oct 11 '22 06:10

Paulw11