【问题标题】:SKProductsRequest returning 0 ProductsSKProducts请求返回 0 个产品
【发布时间】:2018-06-12 17:02:23
【问题描述】:

我正在尝试进行 IAP,但由于某种原因,我的 SKProductsRequest 返回 0 个产品。

-测试产品已添加到iTunes连接正常

-填写银行和税务信息

-产品的捆绑包ID与应用捆绑包ID匹配

-我已经等了两天,以便通过服务器处理它

我使用这个 youtube 教程来构建应用程序: https://www.youtube.com/watch?v=zRrs7O5yjKI

这里是代码:

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.
    }
}

当您运行应用程序时,它会打印“IAP 已启用,正在加载”,而不是“产品请求”,但没有其他内容。

如果我在 productsRequest 函数中打印response.invalidProductIdentifiers,它将返回我的产品:["com.IAPTesters.RemoveAds", "com.IAPTesters.10Dolla"]

提前感谢您的帮助

【问题讨论】:

  • 阅读documentation中的注释;您必须在属性中保留您的产品请求
  • @Paulw11 我仍然不明白需要对代码进行那种更改
  • 您的产品请求对象不能是局部变量,因为它会在函数退出后立即释放(即在请求完成之前)。您需要在属性中保留您的请求。
  • @Paulw11 你指的是我在 productRequests 函数中调用 myProduct 的变量还是 viewDidLoad() 中的 request 变量。

标签: ios in-app-purchase app-store-connect


【解决方案1】:

事实证明,我的银行和税务信息填写不正确。我重新装满了它,然后我不得不等待大约 30 分钟才能让它再次工作。现在一切正常!感谢大家的帮助

【讨论】:

    【解决方案2】:

    如果您参考SKProductsRequest documentation,您会看到:

    注意

    一定要保持对请求对象的强引用;否则,系统可能会在请求完成之前解除分配。

    您的SKProductsRequest 实例是viewDidLoad 中的本地常量。这将在viewDidLoad 退出后立即释放,并且由于产品请求将异步完成,这将在请求完成之前完成,因此您将永远不会收到回调。

    您应该将SKProductsRequest 保留在属性中,以免它被释放。

    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
        }
    

    仅供参考,您对paymentQueueRestoreCompletedTransactionsFinished 的实现不正确;您应该通过.restored 事务状态处理updatedTransactions 中恢复的事务。 paymentQueueRestoreCompletedTransactionsFinished 方法只能用于更新您的 UI 或执行恢复过程完成后所需的任何其他任务。

    【讨论】:

    • 我尝试按照您显示的方式进行操作,但仍然无法打印所有产品。
    • 您的代表现在被呼叫了吗?您是否检查了响应的无效产品属性?
    • 我的委托方法正在被调用...它只是打印“产品请求”,如果我在我的 productsRequest 函数中打印 myProduct.count 它仍然返回 0。
    • 检查无效产品;您的产品标识符可能不正确。您是在实际设备上而不是模拟器上进行测试吗?
    • 是的,我在我的手机和模拟器上测试它,因为在我发布链接的视频中,他能够通过在模拟器中测试它来获得列出的产品,我没有得到无效产品 ID 错误,它只是在“产品请求”后停止打印
    【解决方案3】:

    如果我的理解正确,那么您实施的问题是您在 iTunes 中使用错误的标识符创建了产品。据我所知,产品标识符应该类似于您的应用程序包标识符,后跟产品功能i.e, COM.COMPANYNAME.MYNEWAPPLICATION.REMOVEADS, COM.COMPANYNAME.MYNEWAPPLICATION.10DOLLA

    示例代码如下,

    typealias SSIAPHelperCompletion = (_ result: [SKProduct]?, _ error: Error?) ->Void
    
    class SSIAPHelper: NSObject {
        fileprivate let productsRequest = SKProductsRequest(productIdentifiers: Set(arrayLiteral: "com.companyname.appname.removeads","com.companyname.appname.10dolla"))
    
        fileprivate var completion: SSIAPHelperCompletion?
    
        static let shared = SSIAPHelper()
    
        func requestProducts(completionHandler: @escaping SSIAPHelperCompletion) {
            self.completion = completionHandler
            self.productsRequest.delegate = SSIAPHelper.shared
            self.productsRequest.start()
        }
    }
    
    extension SSIAPHelper: SKProductsRequestDelegate{
        func requestDidFinish(_ request: SKRequest) {
            print(#function)
        }
    
        func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            print(#function, response.products)
            if let lCompletion = self.completion{
                lCompletion(response.products, nil)
            }
        }
    
        func request(_ request: SKRequest, didFailWithError error: Error) {
            print(#function)
        }
    }
    

    您只需从viewDidLoad() 调用以下函数即可获取产品

    SSIAPHelper.shared.requestProducts(completionHandler: { (result, error) in
    
                })
    

    【讨论】:

    • 产品标识符不需要像 com.company.app.inapp-product-id 那样具有反向域模式。这只是使它们独特且易于跟踪的建议。我从客户那里继承了应用内购买标识符的简单名称。
    • @Jayachandra A 我已经仔细检查过,我在 iTunes Connect 中的产品是使用与我的应用程序包 ID 匹配的正确标识符创建的
    猜你喜欢
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多