Skip to main content

Quick diagnosis

Configuration and initialization

Error screen after initialization

Cause: Invalid, expired, or malformed client token. The SDK shows an error screen with a retry button — not a blank screen. Solution: Generate a fresh clientToken from your server for each checkout session. Client tokens are single-use and expire.
If you see invalid-client-token in the error, your token has expired or was already consumed. Generate a new one from your server.

Payment methods not showing

Cause: Multiple possible causes:
  • No payment methods configured in the Primer Dashboard for the given currency/country
  • The SDK only renders payment methods it supports — unsupported types are silently filtered out
  • Client session missing required currencyCode or countryCode fields
Solution: Check the Xcode console for log messages like "Filtering out unregistered payment method: TYPE". Verify your Dashboard configuration matches the currency and country in your client session. When using the composable PrimerPaymentMethods view, read the available methods from session.selection?.state.paymentMethods.

SwiftUI integration

Composable views render nothing

Cause: The composable views — PrimerCardForm, PrimerPaymentMethods, and PrimerVaultedPaymentMethods — resolve their session from the SwiftUI environment. Without the .primerCheckoutSession(_:onCompletion:) modifier above them, they have no session to bind to. They also stay empty until the session reaches .ready.
A composable view without .primerCheckoutSession above it in the hierarchy cannot resolve a session and will not function. This is the most common cause of “nothing renders” issues.
Solution: Hold a PrimerCheckoutSession as a @StateObject and attach the modifier once around your composable views. The modifier injects the session, the derived PrimerCardFormSession, and the PrimerSelectionSession into the environment, calls start() on appear, and tears the session down on disappear:
If you only need the prebuilt screens, use the managed PrimerCheckout view instead — it owns its own session and requires no modifier.

Session re-initializes unexpectedly

Cause: PrimerCheckoutSession is recreated whenever SwiftUI re-evaluates the enclosing body. Storing it in a let, a computed property, or @State (initialized inline) causes a fresh session — and a fresh client-token initialization — on every view update. Solution: Own the session as a @StateObject so SwiftUI keeps a single instance for the lifetime of the view:
The .primerCheckoutSession modifier drives the lifecycle for you — you normally never call start() or cancel() yourself. Call session.refresh() only when you need to reload after a client-session update (for example, an amount or currency change).

Theme or settings not applied

Cause: Creating PrimerCheckoutTheme() or PrimerSettings() inside the SwiftUI body property. SwiftUI re-evaluates body frequently, creating new objects each time. Solution: Define configuration as constants outside the view body:
See Best Practices for more SwiftUI performance tips.

UIKit integration

Delegate callbacks never fire

Cause: PrimerCheckoutPresenterDelegate not set on PrimerCheckoutPresenter.shared before calling presentCheckout, or the delegate object was deallocated.
The delegate is a weak reference. Ensure the delegate object (typically your view controller) is retained for the lifetime of the checkout presentation. If the delegate is deallocated, callbacks silently stop.
Solution: Set the delegate before presenting and implement all required methods:

Checkout not presented

Cause: Calling presentCheckout while already presenting — the call is silently ignored with no visible feedback. Solution: Check PrimerCheckoutPresenter.isPresenting before calling:

Card form

Form shows invalid despite all visible fields being filled

Cause: The form validation considers all configured fields, including billing address fields that may not be visible on screen. Backend configuration (the Dashboard-driven CardFormConfiguration) controls which billing fields are required. Solution: Inspect state.fieldErrors on the PrimerCardFormSession to identify which specific fields are failing validation. The session is handed to each PrimerCardForm slot — observe it with @ObservedObject:
Use session.state.isValid to gate the submit button and session.state.errorMessage(for:) to display a per-field message.

Apple Pay

Apple Pay not available

Cause: Multiple possible causes:
  • Running on the iOS Simulator (Apple Pay requires a real device)
  • No payment cards added to the user’s Wallet
  • Apple Pay not configured in the Primer Dashboard
  • Merchant identifier mismatch between Apple Developer portal and Primer Dashboard
Solution: Test on a physical device with a card added to Wallet. Verify Apple Pay configuration in both the Apple Developer portal and Primer Dashboard. There is no separate Apple Pay view or scope — once you configure Apple Pay via PrimerSettings(paymentMethodOptions:) with a PrimerApplePayOptions(merchantIdentifier:), Apple Pay surfaces automatically as a CheckoutPaymentMethod (with type == "APPLE_PAY") in PrimerPaymentMethods, or as a managed screen in PrimerCheckout. If it never appears, confirm the method is present in session.selection?.state.paymentMethods. See the Apple Pay integration guide.

Installation

SPM package not found

Cause: Build fails with “No such module ‘PrimerSDK’”. Solution: Re-add the SPM package:
  1. In Xcode, go to File > Add Package Dependencies
  2. Enter the Primer SDK repository URL
  3. Select the correct version and add to your target

CocoaPods installation fails

Cause: pod install fails or doesn’t include the Primer SDK. Solution: Verify your Podfile and reinstall:

Minimum deployment target

Cause: Crash or build error on iOS 14 or earlier. Solution: The Primer Checkout iOS SDK requires iOS 15.0 or later. Update your deployment target:

Validation vs payment errors

Understanding the difference helps with proper error handling:
Don’t confuse these two error types. Validation errors prevent form submission and are shown inline. Payment failures occur after the form is submitted and require explicit handling.

Debugging tips

Log the terminal state

The onCompletion closure on PrimerCheckout or the .primerCheckoutSession(_:onCompletion:) modifier fires exactly once with the terminal PrimerCheckoutState:
For the composable flow, log it from the modifier instead:

Inspect available payment methods

Read the available methods from the PrimerSelectionSession once the session is .ready:

Check session readiness

The composable views bind only once the session reaches .ready. Observe session.phase to confirm initialization completed and the child sessions resolved:

Log card form validation errors

Read fieldErrors from the PrimerCardFormSession handed to a PrimerCardForm slot:

Extract diagnostics for support

Getting help

When contacting Primer support, include:
  1. The diagnosticsId from any error callbacks
  2. Your iOS version, Xcode version, and SDK version
  3. Steps to reproduce the issue

See also

Events guide

Event handling patterns

Build a custom card form

Card form tutorial

Best practices

SwiftUI performance tips