Skip to main content
Primer Checkout iOS SDK is currently in beta (v3.0.0-beta.1). The API is subject to change before the stable release.
CardFormDefaults is the namespace that holds the default content for PrimerCardForm’s slots. When you create a card form without supplying a slot, PrimerCardForm falls back to the matching CardFormDefaults helper. You can also call these helpers yourself inside a custom slot to keep the default rendering while wrapping it with your own layout.
@available(iOS 15.0, *)
public enum CardFormDefaults
CardFormDefaults is an empty enum used purely as a namespace for static factory methods. You never instantiate it; you call its static func members and embed the views they return.

How the defaults are wired

PrimerCardForm declares each @ViewBuilder slot with a default argument that points at the matching CardFormDefaults helper:
public init(
  @ViewBuilder cardDetails: @escaping (PrimerCardFormSession) -> CardDetails = { CardFormDefaults.cardDetails($0) },
  @ViewBuilder billingAddress: @escaping (PrimerCardFormSession) -> Billing = { CardFormDefaults.billingAddress($0) },
  @ViewBuilder submitButton: @escaping (PrimerCardFormSession) -> Submit = { CardFormDefaults.submitButton($0) }
)
Each helper receives the PrimerCardFormSession for the current checkout and returns a concrete, config-aware view. Because they return concrete view types, they can satisfy PrimerCardForm’s generic view parameters (CardDetails, Billing, Submit) at the default-value site, where an opaque some View cannot.

Section helpers

These three helpers produce the bodies of PrimerCardForm’s three slots.
MethodReturnsDescription
cardDetails(_:)CardDetailsContentDefault card-details section (number, expiry, CVV, cardholder name).
billingAddress(_:)BillingAddressContentDefault billing-address section. Renders only when the configuration requires billing fields.
submitButton(_:)CardSubmitButtonDefault submit button, enabled when the form is valid and not loading.
public static func cardDetails(_ session: PrimerCardFormSession) -> CardDetailsContent

public static func billingAddress(_ session: PrimerCardFormSession) -> BillingAddressContent

public static func submitButton(_ session: PrimerCardFormSession) -> CardSubmitButton
billingAddress(_:) and the individual billing fields render nothing unless the API-driven form configuration includes billing fields, so it is always safe to include the section. The decision is made for you from the session’s configuration.

unavailable()

PrimerCardForm shows this placeholder when no checkout session is present in the environment — that is, when the .primerCheckoutSession(_:onCompletion:) modifier has not been applied, or the session is not yet ready.
public static func unavailable() -> some View
It returns an EmptyView, so an unconfigured form occupies no space rather than crashing or showing partial UI.

Returned section views

The section helpers return concrete View types. You rarely name these directly — you embed them inside your own layout — but they are public so you can store and compose them.
TypeDescription
CardDetailsContentThe shared, config-aware card-details renderer (number, expiry, CVV, cardholder name, and the co-badged network selector when multiple networks are detected).
BillingAddressContentThe shared, config-aware billing-address renderer. Renders only the fields the configuration requires.
CardSubmitButtonThe default pay button. Disabled when the form is invalid or a submission is in flight.
All three are annotated @available(iOS 15.0, *) and conform to View.

Wrapping the default in a custom slot

The most common reason to call CardFormDefaults directly is to keep the SDK’s rendering while adding your own chrome around it. Pass a slot closure to PrimerCardForm, then call the matching helper inside it.
import SwiftUI
import PrimerSDK

struct CheckoutView: View {
  @StateObject private var session = PrimerCheckoutSession(clientToken: clientToken)

  var body: some View {
    ScrollView {
      PrimerCardForm(
        cardDetails: { formSession in
          VStack(alignment: .leading, spacing: 16) {
            Text("Card details")
              .font(.headline)

            // Keep the default fields, wrapped in your own layout.
            CardFormDefaults.cardDetails(formSession)

            Text("Your card is encrypted and never stored on this device.")
              .font(.footnote)
              .foregroundColor(.secondary)
          }
          .padding()
        }
      )
    }
    .primerCheckoutSession(session) { state in
      handle(state)
    }
  }
}
Because each helper returns a concrete view, you can place it anywhere a SwiftUI view is expected — inside a VStack, a GroupBox, a card-styled container, or alongside your own promotional content.

Replacing only the submit button

To keep the default fields but swap the pay button, supply only the submitButton slot and leave the other two at their defaults:
PrimerCardForm(
  submitButton: { formSession in
    Button {
      formSession.submit()
    } label: {
      Text("Pay now")
        .frame(maxWidth: .infinity)
    }
    .buttonStyle(.borderedProminent)
    .disabled(!formSession.state.isValid || formSession.state.isLoading)
  }
)
Read formSession.state.isValid and formSession.state.isLoading to mirror the enablement logic of the default CardSubmitButton. See PrimerCardFormSession for the full state surface.

Recomposing individual fields

The section helpers are coarse-grained: they render an entire section. For fine-grained layouts, CardFormDefaults also exposes per-field building blocks (cardNumber(_:), cvv(_:), firstName(_:), and so on) on the same namespace. Compose those inside your cardDetails or billingAddress slot to arrange fields exactly how you want.
PrimerCardForm(
  cardDetails: { formSession in
    VStack(spacing: 12) {
      CardFormDefaults.cardNumber(formSession)
      HStack(spacing: 12) {
        CardFormDefaults.expiryDate(formSession)
        CardFormDefaults.cvv(formSession)
      }
      CardFormDefaults.cardholderName(formSession)
    }
  }
)
The per-field building blocks suppress the built-in co-badged network selector, so when you recompose individual card fields you must add CardFormDefaults.cardNetwork(_:) yourself if you support co-badged cards. Do not combine cardNetwork(_:) with the full cardDetails(_:) section — the section already renders the selector when multiple networks are detected, and you would see two selectors.
The per-field building blocks are documented in full on Card field components.

See also

PrimerCardForm

The card form view and its three section slots.

Card field components

Per-field building blocks for fully custom layouts.

PrimerCardFormSession

The observable session that drives form state and submission.