dbmrq dbmrq - 5 months ago 269
iOS Question

How to handle shouldAddStorePayment for In-App Purchases in iOS 11?

I'm trying to implement that new

method so my app can handle IAPs directly from the App Store.

I'm using the
url to test it, like it says here.

The thing is, my
is a specific view controller, and if it's not visible when I open the
link the delegate method won't be called.

What can I do about that? I think I'd have to detect if the user is coming from the App Store to push the right view controller, but I don't know how. The only other option I can think of right now is to make the App Delegate an
, but that seems really cumbersome and I couldn't get it to work when I tried it. Is there any other way?

Jad Jad
Answer Source

Here's a class I did that can help you achieve what you want, simply copy the code below and paste it inside a new file and then you can simply access the class StoreManager.shared to whatever method/variable you want to access.

1- To init this class, just call from your didFinishLaunchingWithOptions StoreManager.shared.Begin() and then payment observer is added.

import Foundation
import StoreKit

class StoreManager: NSObject{

     Initialize StoreManager and load subscriptions SKProducts from Store
    static let shared = StoreManager()

    func Begin(){
        print("StoreManager initialized".DubugText())

    override init() {

        // Add pyament observer to payment qu


    func requestProductWithID(identifers:Set<String>){

        if SKPaymentQueue.canMakePayments() {
            let request = SKProductsRequest(productIdentifiers:
            request.delegate = self

        } else {
            print("ERROR: Store Not Available")


    func buyProduct(product: SKProduct) {

        print("Buying \(product.productIdentifier)...")
        let payment = SKPayment(product: product)


    func restorePurchases(){

     Instance variables



// MARK:
// MARK: SKProductsRequestDelegate

//The delegate receives the product information that the request was interested in.
extension StoreManager:SKProductsRequestDelegate{

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

        var products = response.products as [SKProduct]

        var buys = [SKProduct]()

        if (products.count > 0) {

            for i in 0 ..< products.count
                let product = products[i]

                print("Product Found: ",product.localizedTitle)

        } else {
            print("No products found")

        let productsInvalidIds = response.invalidProductIdentifiers

        for product in productsInvalidIds
            print("Product not found: \(product)")

    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Something went wrong: \(error.localizedDescription)")

// MARK:
// MARK: SKTransactions

extension StoreManager: SKPaymentTransactionObserver {

    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch (transaction.transactionState) {
            case .purchased:
                completeTransaction(transaction: transaction)
            case .failed:
                failedTransaction(transaction: transaction)
            case .restored:
                restoreTransaction(transaction: transaction)
            case .deferred:
                // TODO show user that is waiting for approval

            case .purchasing:

    private func completeTransaction(transaction: SKPaymentTransaction) {


        deliverPurchaseForIdentifier(identifier: transaction.payment.productIdentifier)

    private func restoreTransaction(transaction: SKPaymentTransaction) {

        guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }

        print("restoreTransaction... \(productIdentifier)")

        deliverPurchaseForIdentifier(identifier: productIdentifier)

    private func failedTransaction(transaction: SKPaymentTransaction) {

        if let error = transaction.error as NSError? {
            if error.domain == SKErrorDomain {
                // handle all possible errors
                switch (error.code) {
                case SKError.unknown.rawValue:
                    print("Unknown error")

                case SKError.clientInvalid.rawValue:
                    print("client is not allowed to issue the request")

                case SKError.paymentCancelled.rawValue:
                    print("user cancelled the request")

                case SKError.paymentInvalid.rawValue:
                    print("purchase identifier was invalid")

                case SKError.paymentNotAllowed.rawValue:
                    print("this device is not allowed to make the payment")




    private func deliverPurchaseForIdentifier(identifier: String?) {

        guard let identifier = identifier else { return }


//In-App Purchases App Store
extension StoreManager{

    func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
        return true

        //To hold
        //return false

        //And then to continue

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download