In-App Purchases

Tagged with: Swift, iOS

696 words; 3 minutes to read.

Adding In-App Purchases (IAP) to your app can enhance user experience and generate revenue. Here’s a step-by-step guide to setting up IAP in App Store Connect.

Adding a New Product (IAP)

Distribution Page

  1. Navigate to the Distribution Page:
    • Find the bottom left corner of your app’s Distribution page in App Store Connect.
  2. Add a New Product:
    • Click the plus sign to add a new product (IAP).

Choose the Type of IAP

There are two types of In-App Purchases:

Setting Up Your IAP

Once you’ve filled in the necessary details, save your product. If the system doesn’t automatically take you to the next page, click on your newly saved IAP.


Configure IAP Details

Here, you need to set various options:

Review Information:It’s crucial to fill this section extensively. Detailed information here will help ensure that your review passes the check immediately.

Implementing IAP in Your App

Now let’s move on to the code side of things.

1. Track User Purchases:

Create an observable object to track important behaviors in the app, such as the type of user (free or premium).

class ApplicationState: ObservableObject {
    @Published var route: AppRouter = .splash
    @Published var isPremiumUser: Bool = false
}

And here is IAP manager that can help anyone as a starting point.

import Foundation
import StoreKit

class IAStoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {

    @Published var products: [SKProduct] = []
    @Published var purchasedProductIdentifiers: Set = []

    override init() {
        super.init()
        loadPurchasedProducts()
        SKPaymentQueue.default().add(self)
        fetchProducts()
    }

    deinit {
        SKPaymentQueue.default().remove(self)
    }

    func fetchProducts() {
        let productIdentifiers = Set(["your_product_id"])
        let request = SKProductsRequest(productIdentifiers: productIdentifiers)
        request.delegate = self
        request.start()
    }

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        DispatchQueue.main.async {
            self.products = response.products
        }
    }
    
    func request(_ request: SKRequest, didFailWithError error: any Error) {
        print(error)
    }
    
    func requestDidFinish(_ request: SKRequest) { }

    func purchase(product: SKProduct) {
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                SKPaymentQueue.default().finishTransaction(transaction)
                savePurchasedProduct(identifier: transaction.payment.productIdentifier)
                loadPurchasedProducts()
            case .failed:
                SKPaymentQueue.default().finishTransaction(transaction)
            case .restored:
                SKPaymentQueue.default().finishTransaction(transaction)
                savePurchasedProduct(identifier: transaction.payment.productIdentifier)
                loadPurchasedProducts()
            case .deferred, .purchasing:
                break
            @unknown default:
                break
            }
        }
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: any Error) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: .restoreFailed, object: error.localizedDescription)
        }
    }
    
    func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    private func savePurchasedProduct(identifier: String) {
        var purchasedProducts = UserDefaults.standard.array(forKey: "purchasedProducts") as? [String] ?? []
        purchasedProducts.append(identifier)
        UserDefaults.standard.set(purchasedProducts, forKey: "purchasedProducts")
    }

    private func loadPurchasedProducts() {
        let purchasedProducts = UserDefaults.standard.array(forKey: "purchasedProducts") as? [String] ?? []
        purchasedProductIdentifiers = Set(purchasedProducts)
    }
}