iOS development
in-app purchases
app monetization
iOS app tutorial
mobile app development

How do you add an in-app purchase to an iOS application?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Adding in-app purchases to an iOS app has two parts: product configuration in App Store Connect and transaction handling in the app. The modern iOS implementation uses StoreKit to fetch products, initiate purchases, verify transactions, and unlock entitlements in a way that is safe to repeat and restore.

Configure Products in App Store Connect

Before writing app code, define the products in App Store Connect. At a minimum, you need:

  • an app record
  • the In-App Purchase capability enabled for the app target
  • one or more product identifiers
  • product metadata such as pricing and localization

The product IDs you create there must exactly match the IDs your code requests later.

Fetch Products with StoreKit

A good starting point is a store service that loads the product definitions available to the app.

swift
1import StoreKit
2
3@MainActor
4final class StoreManager: ObservableObject {
5    @Published var products: [Product] = []
6
7    func loadProducts() async throws {
8        let ids = [
9            "com.example.app.pro.upgrade",
10            "com.example.app.coins.100"
11        ]
12        products = try await Product.products(for: ids)
13    }
14}

This gives you Product values you can display in the UI with localized pricing and names.

Start the Purchase Flow

Once you have a Product, call purchase() and inspect the result carefully.

swift
1import StoreKit
2
3@MainActor
4func buy(_ product: Product) async throws {
5    let result = try await product.purchase()
6
7    switch result {
8    case .success(let verification):
9        let transaction = try checkVerified(verification)
10        await unlockEntitlement(for: transaction)
11        await transaction.finish()
12
13    case .userCancelled:
14        break
15
16    case .pending:
17        print("Purchase is pending approval or completion")
18
19    @unknown default:
20        break
21    }
22}
23
24func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
25    switch result {
26    case .verified(let safe):
27        return safe
28    case .unverified:
29        throw StoreError.failedVerification
30    }
31}

The verification step matters. You should not unlock content just because a purchase attempt occurred.

Restore and Observe Transactions

Users expect paid content to restore correctly across reinstalls and devices. You also need to handle transaction updates that arrive outside the exact moment the purchase button was tapped.

swift
1import StoreKit
2
3func observeTransactions() -> Task<Void, Never> {
4    Task.detached {
5        for await update in Transaction.updates {
6            do {
7                let transaction = try checkVerified(update)
8                await unlockEntitlement(for: transaction)
9                await transaction.finish()
10            } catch {
11                print(error)
12            }
13        }
14    }
15}

For manual restore flows, call:

swift
try await AppStore.sync()

That asks the system to re-sync purchases for the signed-in App Store account.

Model Entitlements Explicitly

Your app should separate payment events from access control. A transaction indicates that StoreKit recorded a purchase. An entitlement is your app's decision about what the user may use.

swift
1import StoreKit
2
3@MainActor
4func unlockEntitlement(for transaction: Transaction) async {
5    if transaction.productID == "com.example.app.pro.upgrade" {
6        UserDefaults.standard.set(true, forKey: "isPro")
7    }
8}

In production systems, durable entitlement storage and server-side validation are often preferable to simple local flags, especially for subscriptions and cross-device access.

Test with Sandbox and StoreKit Configuration

Do not test purchases only in production-like conditions. Use StoreKit testing support and sandbox accounts so you can repeat purchase flows, cancellation paths, and restore behavior safely.

This is where many bugs surface: not in the purchase button itself, but in entitlement restoration, pending purchases, and repeated app launches.

Common Pitfalls

A common mistake is hardcoding UI assumptions before the products are loaded. Product metadata should come from StoreKit, not from guessed pricing strings in the app.

Another is unlocking features without checking transaction verification. That creates a security hole and also makes debugging harder.

Developers also often forget to handle transaction updates and restore flows, which leads to users who paid successfully but do not regain access later.

Summary

  • Configure product identifiers in App Store Connect before writing app logic.
  • Fetch products with StoreKit and display the returned metadata.
  • Start purchases through purchase() and verify transactions before unlocking content.
  • Observe transaction updates and support restore flows with AppStore.sync().
  • Treat entitlements as a deliberate app-level model, not just as a button press result.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.