> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Component Architecture Explorer — iOS

> Explore the iOS Checkout SwiftUI view, session, and slot hierarchy interactively.

Explore the iOS Checkout SwiftUI architecture interactively. The iOS Checkout SDK is a SwiftUI slot API: a `PrimerCheckoutSession` owner wired into your view hierarchy with the `.primerCheckoutSession(_:theme:onCompletion:)` modifier, plus composable views (`PrimerCardForm`, `PrimerPaymentMethods`, `PrimerVaultedPaymentMethods`) whose slots default to the `*Defaults` building blocks. Click any view, slot, or field to see its parameters, lifecycle phases, and copyable Swift code examples.

View & Session Hierarchy

**PrimerCheckoutSession**

.ready → composable views via .primerCheckoutSession

**PrimerCardForm**

**cardDetails**

**cardNumber**

**expiryDate**

**cvv**

**cardholderName**

**cardNetwork**

**billingAddress**

**submitButton**

**PrimerPaymentMethods**

**PrimerVaultedPaymentMethods**

View / session

card
Composable view

field
CardFormDefaults building block

Component Details

***

## Two ways to integrate

| Goal | Use |
| - | - |
| Fastest integration, default screens | [`PrimerCheckout`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout) — the fully managed SwiftUI modal. No slots; it renders Primer's default screens end to end. |
| Custom SwiftUI layout and slots | [`PrimerCheckoutSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session) + `.primerCheckoutSession(_:theme:onCompletion:)`, composing [`PrimerCardForm`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form) / [`PrimerPaymentMethods`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods) / [`PrimerVaultedPaymentMethods`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/vaulted-payment-methods). |
| UIKit apps | [`PrimerCheckoutPresenter`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter) + `PrimerCheckoutPresenterDelegate`. |

`PrimerCheckoutSession` is the iOS analog of Android's `PrimerCheckoutHost`. Where Android wraps your layout in a composable host, iOS injects the session into the SwiftUI environment with a view modifier — child views read their per-feature session (`PrimerCardFormSession`, `PrimerSelectionSession`) from `EnvironmentValues`.

***

## Slots and defaults

Every composable view exposes slots that default to a matching `*Defaults` namespace builder. `PrimerCardForm` and `PrimerPaymentMethods` slots are `@ViewBuilder` closures; `PrimerVaultedPaymentMethods` slots return `AnyView`, so wrap custom content in `AnyView(...)`. Override one slot and the rest keep their default rendering — you never re-implement an entire view.

| View | Slots | Defaults namespace |
| - | - | - |
| `PrimerCardForm` | `cardDetails`, `billingAddress`, `submitButton` | [`CardFormDefaults`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-defaults) |
| `PrimerPaymentMethods` | `header`, `method`, `emptyState` | [`PaymentMethodsDefaults`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-defaults) |
| `PrimerVaultedPaymentMethods` | `header`, `item`, `submitButton` | `VaultedPaymentMethodsDefaults` |

`CardFormDefaults` also exposes per-field building blocks for recomposing the card section one field at a time: `cardNumber`, `expiryDate`, `cvv`, `cardholderName`, `cardNetwork`, plus the billing fields `firstName`, `lastName`, `phoneNumber`, `addressLine1`, `addressLine2`, `city`, `state`, `postalCode`, `countryCode`. Every block except `cardNetwork` is self-hiding — it renders nothing unless its field is part of the active [`CardFormConfiguration`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session). `cardNetwork` is not tied to the configuration: it renders only when more than one card network is detected for the entered card number. `cardNumber` already shows the co-badged network selector inside the card number field. If you add `cardNetwork` next to it, two selectors appear. Field presence is Dashboard-driven; visual styling comes from the [`PrimerCheckoutTheme`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/theming/primer-theme) design tokens.

***

## Sessions and state

Composable views are driven by observable sessions injected into the environment by `.primerCheckoutSession(_:theme:onCompletion:)`. Observe them with `@ObservedObject` and read their published properties.

| Session | Drives | Published state |
| - | - | - |
| [`PrimerCheckoutSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session) | The whole flow (owner) | `phase` (`.initializing` / `.ready`), `clientSession` (`PrimerClientSession?`, non-nil from `.ready`) |
| [`PrimerCardFormSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session) | `PrimerCardForm` slots | `PrimerCardFormState` |
| [`PrimerSelectionSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-session) | `PrimerPaymentMethods`, `PrimerVaultedPaymentMethods` | `state` (`PrimerPaymentMethodSelectionState`), `vaultedPaymentMethods` |

The outcome of the flow is delivered through the modifier's `onCompletion` closure as a [`PrimerCheckoutState`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state). `.failure` is delivered once per failed attempt: when a payment fails or a `refresh()` fails, the checkout stays usable so the shopper can retry. A failure while the checkout is still loading ends the flow, and nothing more is delivered. `.success` or `.dismissed` is delivered at most once, after which nothing more is delivered. `.initializing` and `.ready` are never delivered to `onCompletion`. Read the lifecycle from `PrimerCheckoutSession.phase` and `clientSession`.

| State | Associated values | Description |
| - | - | - |
| `.initializing` | — | Loading configuration and payment methods. Not delivered to `onCompletion`; see `phase`. |
| `.ready` | `clientSession:` [`PrimerClientSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state#primerclientsession) | Payment methods loaded. Carries the order total, currency, line items, customer and fees. Not delivered to `onCompletion`; read `session.clientSession`. |
| `.success` | `PaymentResult` | Payment completed. Carries `paymentId`, `status`, and other details. |
| `.failure` | `PrimerError`, `checkoutData: PrimerCheckoutData?` | Payment or checkout failed. |
| `.dismissed` | — | Checkout was dismissed without completing a payment. |

### PrimerCardFormState

| Property | Type | Description |
| - | - | - |
| `configuration` | `CardFormConfiguration` | Which fields the form requires (Dashboard-driven). |
| `data` | `FormData` | Current field values keyed by `PrimerInputElementType`. For PCI safety, the card number keeps only its first 6 and last 4 digits, masked with `•`. The CVV is always an empty string. |
| `fieldErrors` | `[FieldError]` | Validation errors per field. Empty when valid. |
| `isLoading` | `Bool` | `true` while a payment is being submitted. |
| `isValid` | `Bool` | `true` when all required fields pass validation. |
| `selectedCountry` | `PrimerCountry?` | Selected billing country, or `nil`. |
| `selectedNetwork` | `PrimerCardNetwork?` | Selected network for co-badged cards, or `nil`. |
| `availableNetworks` | `[PrimerCardNetwork]` | Detected networks; more than one indicates a co-badged card. |
| `surchargeAmountRaw` | `Int?` | Surcharge for the selected network in minor units (for example cents), or `nil` when no surcharge applies. |
| `surchargeAmount` | `String?` | Formatted surcharge for the selected network, ready to display. It already starts with a plus sign (for example `+ $1.50`), or is `nil` when no surcharge applies. |
| `binData` | `PrimerBinData?` | BIN lookup result for the entered card: `preferred`, `alternatives`, `status` (`.partial` or `.complete`) and `firstDigits`, or `nil`. Once `status` is `.complete`, `preferred` and `alternatives` follow the `orderedAllowedCardNetworks` order of your client session. |

***

## Composing custom UI

Hold the session as a `@StateObject`, place the composable views in your layout, and attach the modifier. The child views resolve their per-feature session from the environment — you do not pass a session into them.

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

@available(iOS 15.0, *)
struct CheckoutView: View {
  @StateObject private var session: PrimerCheckoutSession

  init(clientToken: String) {
    _session = StateObject(
      wrappedValue: PrimerCheckoutSession(clientToken: clientToken)
    )
  }

  var body: some View {
    ScrollView {
      VStack(spacing: 24) {
        PrimerPaymentMethods()
        PrimerCardForm(
          cardDetails: { formSession in
            VStack(spacing: 16) {
              // Shows the co-badged network selector itself when multiple networks are detected.
              CardFormDefaults.cardNumber(formSession)
              HStack(spacing: 16) {
                CardFormDefaults.expiryDate(formSession)
                CardFormDefaults.cvv(formSession)
              }
              CardFormDefaults.cardholderName(formSession)
            }
          },
          submitButton: { formSession in
            Button("Pay now") { formSession.submit() }
              .buttonStyle(.borderedProminent)
              .disabled(!formSession.state.isValid || formSession.state.isLoading)
          }
        )
      }
      .padding()
    }
    .primerCheckoutSession(session) { state in
      handle(state)
    }
  }

  private func handle(_ state: PrimerCheckoutState) {
    switch state {
    case let .success(result):
      print("Payment \(result.paymentId) succeeded")
    case let .failure(error, _):
      print("Checkout failed: \(error.localizedDescription)")
    case .dismissed:
      print("Checkout dismissed")
    default:
      break
    }
  }
}
```

> [!WARNING]
>
> Without the `.primerCheckoutSession(_:theme:onCompletion:)` modifier above them in the hierarchy, composable views such as `PrimerCardForm` and `PrimerPaymentMethods` cannot resolve a session and will not function.

***

## Related

* [API overview](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/overview.md) — Full iOS Checkout API reference
* [PrimerCheckoutSession](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session.md) — The session owner and the `.primerCheckoutSession` modifier
* [PrimerCardForm](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form.md) — Slot-based card form
* [Design Tokens Explorer](https://primer.io/docs/checkout/primer-checkout/build-your-ui/design-tokens-explorer-ios.md) — Explore theme tokens
* [Card Form Layout Builder](https://primer.io/docs/checkout/primer-checkout/build-your-ui/card-form-layout-builder.md) — Build card form layouts


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.