VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Swift SDK
Swift quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
Swift SDK reference
Errors
Diagnostic codes
Troubleshooting

Swift quickstart

Add Voidhash to a native iOS app, sell a product and unlock a perk.

Install the Swift SDK in an existing iOS app, buy a product, and unlock a perk with a test purchase. The test purchase runs in development mode, so you don't need App Store Connect yet.

If another billing SDK already sells in your app, see Run in observer mode first.

Before you start

  • Your app targets iOS 15 or later and builds with Xcode and a Swift 6 toolchain.
  • In Studio, you have a project with a perk premium and a product pro_monthly that unlocks it and has a billing duration. See Products and perks.

Set up

Install the SDK

In Xcode, choose File → Add Package Dependencies…, enter https://github.com/voidhashcom/voidhash, and set the dependency rule to the main branch. Add the Voidhash and VoidhashCore libraries to your app target. VoidhashCore holds the model and error types, such as VoidhashStoreError.

To pin a commit or install with CocoaPods, see Install.

Configure the client

Configure Voidhash once, in your App initializer:

MyApp.swift
import SwiftUI
import Voidhash

@main
struct MyApp: App {
    init() {
        var options = VoidhashOptions()

        #if DEBUG
            options.dev = true
            let publishableKey = "vh_pk_test_..."
        #else
            let publishableKey = "vh_pk_..."
        #endif

        Voidhash.configure(publishableKey: publishableKey, options: options)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Debug builds use the test publishable key and buy from a simulated store; release builds use the production key and the App Store. Copy both keys from Settings → API keys. See API keys.

configure returns at once and initializes in the background. In a UIKit app, call it from application(_:didFinishLaunchingWithOptions:). Elsewhere, reach the client through Voidhash.shared.

Show products and buy

Load pro_monthly and buy it when the user taps the button:

ContentView.swift
import SwiftUI
import Voidhash
import VoidhashCore

struct ContentView: View {
    @State private var product: VoidhashProduct?
    @State private var hasPremium = false
    @State private var message: String?

    var body: some View {
        VStack(spacing: 16) {
            Text(hasPremium ? "Premium unlocked" : "Free plan")
            if let product, !hasPremium {
                Button("Upgrade for \(product.displayPrice)") {
                    Task { await buy(product) }
                }
            }
            if let message {
                Text(message)
            }
        }
        .task {
            let products = (try? await Voidhash.shared?.getProducts()) ?? []
            product = products.first { $0.slug == "pro_monthly" }
            await refreshAccess()
        }
    }

    func buy(_ product: VoidhashProduct) async {
        guard let voidhash = Voidhash.shared else { return }
        do {
            switch try await voidhash.purchase(product: product) {
            case .completed: message = nil
            case .deferred: message = "Purchase received. Unlocking soon."
            }
        } catch let error as VoidhashStoreError where error.code == "USER_CANCELLED" {
            return
        } catch let error as VoidhashStoreError where error.code == "PURCHASE_PENDING" {
            message = "Waiting for approval."
        } catch {
            message = "Purchase failed: \(error.localizedDescription)"
        }
        await refreshAccess()
    }

    func refreshAccess() async {
        let person = try? await Voidhash.shared?.getCurrentPerson()
        hasPremium = person?.hasActivePerk("premium") ?? false
    }
}

purchase(product:) shows the App Store sheet, syncs the purchase with Voidhash, and only then finishes the transaction. It returns .completed, or .deferred when the App Store charged but Voidhash hasn't confirmed yet. A closed sheet throws USER_CANCELLED, which isn't a failure; PURCHASE_PENDING waits for approval, such as Ask to Buy. The SDK unlocks the perk once a deferred or pending purchase completes. See Make and restore purchases.

Check access

refreshAccess() gates the feature on the perk, not on the purchase call returning:

let person = try await voidhash.getCurrentPerson()
let hasPremium = person?.hasActivePerk("premium") ?? false

The SDK refreshes the person after every purchase and restore. See Check access for caching and offline behavior.

Test a purchase

Run a debug build on a device or in the Simulator. Tap Upgrade: a test confirmation sheet replaces the App Store's, and the text changes to "Premium unlocked". The purchase appears in Studio under the Test environment.

Buy through the App Store (optional)

Once the simulated purchase works, test the real App Store sheet. Sandbox accounts aren't charged.

  1. Connect the App Store (Help center) and map pro_monthly to your App Store product.
  2. Create a Sandbox Apple Account in App Store Connect and sign in to it on a device.
  3. Run a release build, which uses the production key without dev, or install it from TestFlight.

The purchase appears in Studio under Sandbox. Purchases from an Xcode StoreKit configuration file aren't sent to Voidhash. See Test purchases (Help center).

Next steps

  • See a complete sample app: products, restore, a paywall with a fallback screen, sign-in and a feature flag.
  • Identify users when they sign in.
  • Troubleshooting when something doesn't work.
  • Show a paywall designed in Studio

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousSwift SDKNextDisplay a paywall

On this page

Before you startSet upInstall the SDKConfigure the clientShow products and buyCheck accessTest a purchaseBuy through the App Store (optional)Next steps