> ## 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.

# PrimerCardFormSession

> Observable card form session: field updates, validation, network selection, and submission

<Warning>
  Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.1).
  The API is subject to change before the stable release.
</Warning>

`PrimerCardFormSession` is the observable session for the card payment form. It publishes the current
[`PrimerCardFormState`](#primercardformstate) and exposes field update methods, co-badge network
selection, and submission controls.

The session is created by the SDK and injected into every [`PrimerCardForm`](/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form)
slot closure. Read `state` to render your UI and call the session's methods to mutate the form.

```swift theme={"dark"}
@available(iOS 15.0, *)
@MainActor
public final class PrimerCardFormSession: ObservableObject
```

<Info>
  You never instantiate `PrimerCardFormSession` yourself. Each `PrimerCardForm` slot
  (`cardDetails`, `billingAddress`, `submitButton`) receives the session as a closure parameter.
  Observe it with `@ObservedObject`.
</Info>

## Accessing the session

The session is passed to each `PrimerCardForm` slot. Capture it and observe `state` to drive your UI:

```swift theme={"dark"}
PrimerCardForm(
    submitButton: { session in
        Button("Pay") { session.submit() }
            .disabled(!session.state.isValid || session.state.isLoading)
    }
)
```

To observe state changes inside a custom subview, mark the captured session as `@ObservedObject`:

```swift theme={"dark"}
struct SubmitButton: View {
    @ObservedObject var session: PrimerCardFormSession

    var body: some View {
        Button("Pay") { session.submit() }
            .disabled(!session.state.isValid || session.state.isLoading)
    }
}
```

## Property

| Property | Type                  | Description                                                                                                                                                                                                     |
| -------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state`  | `PrimerCardFormState` | The latest card form state. `@Published private(set)` — read-only, observe it through `@ObservedObject`. Contains field values, validation errors, loading state, network selection, and surcharge information. |

## Methods

### Field update methods

Each setter forwards a raw string to the form. The session re-validates and re-publishes `state`
after every update. Updating the card number triggers network detection and may populate
`state.availableNetworks` and `state.binData`.

| Method                                  | Description                                            |
| --------------------------------------- | ------------------------------------------------------ |
| `updateCardNumber(_ value: String)`     | Updates the card number. Triggers network detection.   |
| `updateCvv(_ value: String)`            | Updates the CVV / security code.                       |
| `updateExpiryDate(_ value: String)`     | Updates the expiry date (MM/YY).                       |
| `updateCardholderName(_ value: String)` | Updates the cardholder name.                           |
| `updatePostalCode(_ value: String)`     | Updates the billing postal / ZIP code.                 |
| `updateCountryCode(_ value: String)`    | Updates the billing country code (ISO 3166-1 alpha-2). |
| `updateCity(_ value: String)`           | Updates the billing city.                              |
| `updateState(_ value: String)`          | Updates the billing state / region.                    |
| `updateAddressLine1(_ value: String)`   | Updates billing address line 1.                        |
| `updateAddressLine2(_ value: String)`   | Updates billing address line 2.                        |
| `updatePhoneNumber(_ value: String)`    | Updates the billing phone number.                      |
| `updateFirstName(_ value: String)`      | Updates the billing first name.                        |
| `updateLastName(_ value: String)`       | Updates the billing last name.                         |

### Network selection

| Method                                            | Description                                                                                                                                                                                                  |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `selectCardNetwork(_ network: PrimerCardNetwork)` | Selects a network for co-badged cards. Applicable only when `state.availableNetworks` contains more than one network. See [PrimerCardNetwork](/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-card-network). |

### Lifecycle

| Method     | Description                                                                                        |
| ---------- | -------------------------------------------------------------------------------------------------- |
| `submit()` | Submits the form for tokenization and payment. Sets `state.isLoading` to `true` during submission. |
| `cancel()` | Cancels the active card form flow.                                                                 |

<Tip>
  When wiring custom `TextField`s, drive each field with the matching `update*` method and read
  back the current value from `session.state.data` so the form stays the single source of truth.
</Tip>

```swift theme={"dark"}
PrimerCardForm(
    cardDetails: { session in
        TextField("Card number", text: Binding(
            get: { session.state.data[.cardNumber] },
            set: { session.updateCardNumber($0) }
        ))
    }
)
```

## PrimerCardFormState

```swift theme={"dark"}
@available(iOS 15.0, *)
public struct PrimerCardFormState: Equatable
```

The complete state of the card payment form: configuration, current field values, validation errors,
loading and validity flags, co-badge network selection, and surcharge information.

### Properties

| Property             | Type                    | Description                                                                                                       |
| -------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `configuration`      | `CardFormConfiguration` | Which fields the form requires. See [CardFormConfiguration](#cardformconfiguration).                              |
| `data`               | `FormData`              | Current field values keyed by `PrimerInputElementType`. See [FormData](#formdata).                                |
| `fieldErrors`        | `[FieldError]`          | Validation errors per field. Empty when there are no errors. See [FieldError](#fielderror).                       |
| `isLoading`          | `Bool`                  | `true` while a payment is being submitted.                                                                        |
| `isValid`            | `Bool`                  | `true` when all required fields pass validation. Updates in real time.                                            |
| `selectedCountry`    | `PrimerCountry?`        | Selected billing country, or `nil`. See [PrimerCountry](/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-country). |
| `selectedNetwork`    | `PrimerCardNetwork?`    | Currently selected network for co-badged cards, or `nil`.                                                         |
| `availableNetworks`  | `[PrimerCardNetwork]`   | Card networks detected from the card number. More than one indicates a co-badged card.                            |
| `surchargeAmountRaw` | `Int?`                  | Surcharge amount in the smallest currency unit (e.g. cents), or `nil`.                                            |
| `surchargeAmount`    | `String?`               | Formatted surcharge amount for display, or `nil`.                                                                 |
| `binData`            | `PrimerBinData?`        | BIN metadata detected from the card number, or `nil`.                                                             |

### Convenience properties

| Property        | Type                       | Description                                                                        |
| --------------- | -------------------------- | ---------------------------------------------------------------------------------- |
| `displayFields` | `[PrimerInputElementType]` | All fields that should be rendered (card fields plus billing fields when enabled). |

### Helper methods

| Method                                                           | Description                                                                          |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `hasError(for fieldType: PrimerInputElementType) -> Bool`        | Returns `true` when the given field currently has a validation error.                |
| `errorMessage(for fieldType: PrimerInputElementType) -> String?` | Returns the human-readable error message for the field, or `nil` when there is none. |

```swift theme={"dark"}
PrimerCardForm(
    cardDetails: { session in
        VStack(alignment: .leading) {
            CardFormDefaults.cardNumber(session)
            if session.state.hasError(for: .cardNumber),
               let message = session.state.errorMessage(for: .cardNumber) {
                Text(message).foregroundColor(.red)
            }
        }
    }
)
```

<Info>
  Validation errors surface in `fieldErrors` after the form evaluates a field. Use `isValid` to
  gate the submit button and `errorMessage(for:)` to display per-field messages. See
  [ValidationResult](/docs/sdk/ios-checkout/v3.0.0-beta/api/common/validation-result).
</Info>

## CardFormConfiguration

```swift theme={"dark"}
@available(iOS 15.0, *)
public struct CardFormConfiguration: Equatable
```

Declares which fields the card form requires.

| Property                 | Type                       | Description                                                                |
| ------------------------ | -------------------------- | -------------------------------------------------------------------------- |
| `cardFields`             | `[PrimerInputElementType]` | Card-specific fields (card number, expiry, CVV, cardholder name).          |
| `billingFields`          | `[PrimerInputElementType]` | Billing address fields. Empty when billing address collection is disabled. |
| `requiresBillingAddress` | `Bool`                     | `true` when billing address collection is enabled.                         |
| `allFields`              | `[PrimerInputElementType]` | Computed: `cardFields + billingFields`.                                    |

```swift theme={"dark"}
public init(
    cardFields: [PrimerInputElementType],
    billingFields: [PrimerInputElementType] = [],
    requiresBillingAddress: Bool = false
)
```

The static `CardFormConfiguration.default` requires `[.cardNumber, .expiryDate, .cvv, .cardholderName]`
with no billing fields.

## FormData

```swift theme={"dark"}
@available(iOS 15.0, *)
public struct FormData: Equatable
```

Type-safe container for field values keyed by `PrimerInputElementType`. Subscript access returns an
empty string for unset fields.

| Member                                         | Type                               | Description                                                     |
| ---------------------------------------------- | ---------------------------------- | --------------------------------------------------------------- |
| `subscript(fieldType: PrimerInputElementType)` | `String`                           | Reads or writes the value for a field. Returns `""` when unset. |
| `dictionary`                                   | `[PrimerInputElementType: String]` | All current field values as a dictionary.                       |

```swift theme={"dark"}
let cardNumber = session.state.data[.cardNumber]
let allValues = session.state.data.dictionary
```

## FieldError

```swift theme={"dark"}
@available(iOS 15.0, *)
public struct FieldError: Equatable, Identifiable
```

A validation error for a specific form field.

| Property    | Type                     | Description                                                      |
| ----------- | ------------------------ | ---------------------------------------------------------------- |
| `id`        | `PrimerInputElementType` | Stable identifier (the field type) for SwiftUI diffing.          |
| `fieldType` | `PrimerInputElementType` | The field that has the error.                                    |
| `message`   | `String`                 | Human-readable error message to display.                         |
| `errorCode` | `String?`                | Machine-readable error code for programmatic handling, or `nil`. |

```swift theme={"dark"}
ForEach(session.state.fieldErrors) { error in
    Text(error.message)
}
```

## PrimerInputElementType

The field identifier used throughout `PrimerCardFormState` (in `configuration`, `data`, and
`fieldErrors`).

| Case              | Field                  |
| ----------------- | ---------------------- |
| `.cardNumber`     | Card number            |
| `.expiryDate`     | Expiry date (MM/YY)    |
| `.cvv`            | CVV / security code    |
| `.cardholderName` | Cardholder name        |
| `.postalCode`     | Billing postal code    |
| `.countryCode`    | Billing country        |
| `.city`           | Billing city           |
| `.state`          | Billing state / region |
| `.addressLine1`   | Billing address line 1 |
| `.addressLine2`   | Billing address line 2 |
| `.phoneNumber`    | Billing phone number   |
| `.firstName`      | Billing first name     |
| `.lastName`       | Billing last name      |
| `.email`          | Email address          |
| `.otp`            | One-time passcode      |
| `.retailer`       | Retailer               |
| `.all`            | All fields             |
| `.unknown`        | Unknown / unspecified  |

## See also

<CardGroup cols={2}>
  <Card title="PrimerCardForm" icon="credit-card" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form">
    The card form component and its customizable slots.
  </Card>

  <Card title="Card field components" icon="puzzle-piece" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-field-components">
    Prebuilt field views from CardFormDefaults.
  </Card>

  <Card title="ValidationResult" icon="circle-check" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/common/validation-result">
    Field validation model and error handling.
  </Card>

  <Card title="PrimerCountry" icon="globe" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-country">
    Country model for billing address selection.
  </Card>
</CardGroup>
