iOS SDK
The iOS SDK is the native counterpart of the web Headless Checkout and the sibling of the Android SDK: the same concepts (client session, payment methods, primary and secondary methods, split, submit) in Swift. You build the checkout screen; the SDK exposes the session's payment methods as observable state, renders each method's own step (a secure card form, Apple Pay's sheet, Klarna's widget, a PSP page) and drives the payment to its outcome.
How it works
Key characteristics
| Aspect | Detail |
|---|---|
| Integration | @Published properties on an ObservableObject for methods, tokens, amounts and session state; one type, PaymentElement, renders any method |
| UI control | You build the whole checkout screen; the SDK renders only each method's own step |
| Payment methods | Card (Purse vault), Apple Pay, PayPal, Klarna, every redirection method the session offers, gift cards, saved cards and saved PayPal accounts, BNPL instalment simulation |
| Compliance | Card data is captured by the embedded iOS Secure Fields and never reaches your code |
| Distribution | Swift Package Manager: one library product plus one optional product per partner SDK |
| Web parity | Same session, same /init and /validate contract, same error codes and method states as the web SDK |
Products
| Product | Contents | Add it when |
|---|---|---|
PurseHeadlessCheckout | Engine, payment elements, card and redirection methods, gift cards, saved methods | Always |
PurseCheckoutApplePay | Apple Pay plugin and PassKit | You offer Apple Pay |
PurseCheckoutKlarna | Klarna plugin and the Klarna Mobile SDK | You offer Klarna |
PurseCheckoutPayPal | PayPal plugin and the PayPal iOS SDK | You offer PayPal |
A merchant offering cards only links none of the partner SDKs, embeds no PassKit and declares no Apple Pay entitlement. The four products release together and share one version number.
Unlike Android, where a partner artifact registers itself from the classpath, iOS has no ServiceLoader. Each partner product you link must be installed once at startup:
PluginRegistry.shared.install(ApplePayModule(configuration: applePayConfiguration))
PluginRegistry.shared.install(PayPalModule())
PluginRegistry.shared.install(KlarnaModule())
Apple Pay's merchant identifier is bound to your app's entitlement, so it can only come from your app. What each partner needs from your project is on its own page under Payment methods.
Requirements
| Requirement | Value |
|---|---|
| Minimum iOS | 16.0 |
| Swift | 6.0. The package builds in Swift 6 language mode with strict concurrency |
| Xcode | 16+ |
| UI | SwiftUI or UIKit. Each payment element is a UIView, wrapped for SwiftUI by PaymentElementView |
| Client session | Created by your backend with the Purse API; the app receives its widget.data string |
PurseHeadlessCheckout is annotated @MainActor: create it, read its state and call it from the main actor. Every network call it makes is async and runs off the main thread by construction.
When to use this SDK
| Scenario | Recommendation |
|---|---|
| Native iOS checkout with your own design | ✅ iOS SDK |
| Native Android checkout with your own design | Android SDK |
| Web checkout with your own design | Headless Checkout (web) |
| Collect card data only and pay server-side | iOS Secure Fields |
| Fastest integration, standard checkout | Drop-in Checkout in a web view, or Hosted Page |
What you build vs. what the SDK handles
- SDK handles
- You build
- Decoding the client session and listing what it offers
- Each method's own step: secure card form, Apple Pay sheet, PayPal browser switch, Klarna widget, PSP page
/initand/validatecalls, 3-D Secure and redirect returns- Amount split between a primary method and gift cards
- Saved cards and accounts, wallet management, BNPL simulation
- Refusing a double submit, an expired or an already paid session
- Snapshot and restore across process death (
saveState()/restore(_:))
- The checkout screen and its navigation
- The method list and the selection UI
- The Pay button (except for Apple Pay and PayPal, which bring their own)
- Loading, success and failure screens
- Confirming the final payment status on your backend, from webhooks or Get a client session
- Styling of the card form through the theme
Differences from the Android SDK
The two native SDKs are deliberately the same shape. Where they differ, it is because the platform forced it:
| Topic | Android | iOS |
|---|---|---|
| Partner discovery | ServiceLoader finds an artifact on the classpath | PluginRegistry.install(_:), called by your app |
| Wallet | Google Pay | Apple Pay, configured from the session and your merchant identifier |
| Reactive state | StateFlow | @Published on an ObservableObject |
| Rendering | One composable, rememberPaymentElement | PaymentElement, wrapped by PaymentElementView for SwiftUI |
| Theming | CSS-like strings parsed into values | Native UIColor, UIFont and CGFloat |
The full list, including every deliberate divergence and its reason, is kept in the SDK repository's own web ↔ Android ↔ iOS map.
How this documentation is organized
Like the other Purse SDKs, this section follows the Diátaxis framework: each page is one of four kinds, and never mixes them.
| Kind | Read it to | Pages |
|---|---|---|
| Tutorial | Learn by doing | Getting started: a card payment in sandbox from an empty project |
| How-to guides | Solve one task | Payment methods, one guide per method · Advanced guides · Error handling · Run the sample app |
| Reference | Look something up | SDK reference · Error codes · Method states · Supported payment methods · Changelog |
| Explanation | Understand the model | This page · Key concepts |
Known limitations
- Purse 3-D Secure is not supported yet. Card payments are authenticated only by the PSP's own 3-D Secure, which the SDK presents as a redirection. 3-D Secure run by Purse, as the Web SDK does with
threeDSChallenge, has no equivalent in this SDK.