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

# Troubleshooting

> Common issues and solutions for the Primer Checkout iOS SDK

> [!WARNING]
>
> Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.8).
> The API is subject to change before the stable release.

## Installation issues

### Swift Package Manager fails to resolve

**Problem**: Xcode reports that it cannot resolve `primer-sdk-ios`, or the package gets stuck while resolving versions.

**Solution**: Confirm the package URL is `https://github.com/primer-io/primer-sdk-ios.git` and that the dependency rule targets the `3.0.0-beta.8` beta. Then reset Xcode's cached package state:

1. **File ▸ Packages ▸ Reset Package Caches**
2. **File ▸ Packages ▸ Resolve Package Versions**

If resolution still fails, close Xcode and clear the derived data and SPM caches:

```bash theme={"dark"}
rm -rf ~/Library/Developer/Xcode/DerivedData
rm -rf ~/Library/Caches/org.swift.swiftpm
```

> [!NOTE]
>
> The Primer Checkout iOS SDK requires **iOS 15.0+**. If your target's deployment version is lower, SPM cannot satisfy the platform requirement and resolution fails.

### CocoaPods: bundle code-signing error on Xcode 14

**Problem**: After `pod install`, the build fails with an error stating that a resource bundle needs to be code signed.

**Solution**: Add the following `post_install` script to your `Podfile`, then run `pod install` again. It disables code signing on the SDK's resource bundles, which do not require signing:

```ruby theme={"dark"}
post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
      target.build_configurations.each do |config|
        config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
      end
    end
  end
end
```

After editing the `Podfile`, always run `pod install` again and reopen the generated `.xcworkspace` rather than the `.xcodeproj`.

See the [Installation](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/installation.md) guide for the full setup steps.

## Runtime issues

### Checkout stays in the initializing phase

**Problem**: `PrimerCheckoutSession.phase` never transitions from `.initializing` to `.ready`, so your composable views show only the loading state.

**Possible causes**:

| Cause | What to check |
| - | - |
| Invalid or expired client token | Generate a fresh client token immediately before presenting checkout. Tokens are short-lived. |
| Network connectivity | Confirm the device has connectivity and that your backend can reach Primer's API. |
| Incorrect API key | Verify the API key used to create the [client session](https://primer.io/docs/checkout/client-session.md) belongs to the correct environment (sandbox vs. production). |

**Solution**: When initialization fails, the session delivers a `.failure` with a `PrimerError` through the `onCompletion` closure on `.primerCheckoutSession(_:theme:onCompletion:)`. Inspect that error and log its `diagnosticsId`:

```swift theme={"dark"}
.primerCheckoutSession(session) { state in
    if case let .failure(error, _) = state {
        print("Checkout failed: \(error.localizedDescription)")
        print("Diagnostics ID: \(error.diagnosticsId)")
    }
}
```

### Checkout fails at start with an invalid-value error

**Problem**: The checkout shows the error screen right away, or `onCompletion` receives a `.failure` whose `PrimerError` has `errorId` `invalid-value`. The description names the key `paymentHandling`.

**Cause**: `PrimerSettings(paymentHandling: .manual)`. Primer Checkout creates the payment itself and does not support manual payment handling.

**Solution**: Use the default `.auto`. To pass an idempotency key or to abort a payment before the SDK creates it, set [`onBeforePaymentCreate`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session). See [PrimerPaymentHandling](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-settings.md#primerpaymenthandling).

### Payment methods not showing

**Problem**: No payment methods appear, or a payment method you enabled is missing from the list. If no method is available, the session never reaches `.ready`, `PrimerPaymentMethods` renders nothing, and `onCompletion` receives a `.failure`. Its `PrimerError` has `errorId` `misconfigured-payment-methods` or `missing-configuration`.

**Possible causes**:

* No payment methods are configured in the Primer Dashboard.
* The client session is missing required fields.
* Payment methods are not enabled for the requested currency or country.
* The payment method is not supported by Primer Checkout on iOS. Primer Checkout hides payment methods it cannot present.
* Klarna is missing. Primer Checkout lists Klarna only if your app adds the `PrimerKlarnaSDK` pod. See the [Installation](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/installation.md) guide. Adyen Klarna does not need it.

**Solution**: Verify your [Primer Dashboard](https://dashboard.primer.io) configuration, and ensure the [client session](https://primer.io/docs/checkout/client-session.md) includes the correct `currencyCode` and `countryCode`. Primer filters payment methods server-side based on these values, and the SDK then keeps only the methods it supports. If Primer returns no payment methods, checkout fails with `misconfigured-payment-methods`. If Primer Checkout supports none of the returned methods, checkout fails with `missing-configuration`.

### Card form fields not appearing

**Problem**: Some card form fields — such as cardholder name or billing address fields — do not appear in `PrimerCardForm`.

**Explanation**: The visible fields are driven by your Primer Dashboard configuration, surfaced through the session's published state. Each default field building block self-hides unless its field is present in that configuration, so only fields enabled in the Dashboard appear. This is expected behavior, not a bug.

> [!TIP]
>
> To branch on the active configuration inside a slot, read it from the session state, for example `session.state.configuration.requiresBillingAddress`.

### 3DS challenge not completing

**Problem**: A payment gets stuck during 3D Secure authentication.

**Solution**: 3DS is handled automatically by the SDK. Make sure you do not dismiss or recreate the hosting view while the challenge is on screen — tearing down the view tears down the session (see below). The SDK does not apply its own timeout to the payment, so it keeps waiting while the challenge is on screen. A challenge that fails arrives like any other failed attempt: a `.failure(error, checkoutData)` in `onCompletion` (SwiftUI) or `primerCheckoutPresenterDidFailWithError(_:checkoutData:)` (UIKit). The payment was created before the challenge started, so `checkoutData` carries its payment id. See the [Handle payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md) guide.

## SwiftUI-specific issues

### Session re-created on every render

**Problem**: Checkout reinitializes repeatedly, flickering back to `.initializing`, because a new `PrimerCheckoutSession` is allocated each time the view's `body` is evaluated.

**Solution**: Own the session with `@StateObject` so SwiftUI keeps a single instance across re-renders. Never construct it inline in `body`, inside a conditional, or in a callback:

```swift theme={"dark"}
// Correct — one session for the lifetime of the view
struct CheckoutView: View {
    @StateObject private var session: PrimerCheckoutSession

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

    var body: some View {
        ScrollView {
            PrimerPaymentMethods()
            PrimerCardForm()
        }
        .primerCheckoutSession(session) { state in handle(state) }
    }
}
```

```swift theme={"dark"}
// Incorrect — allocates a new session on every render
struct CheckoutView: View {
    var body: some View {
        let session = PrimerCheckoutSession(clientToken: token) // Don't do this
        return ScrollView {
            PrimerCardForm()
        }
        .primerCheckoutSession(session)
    }
}
```

> [!WARNING]
>
> The `.primerCheckoutSession(_:theme:onCompletion:)` modifier bootstraps the session on appear and tears it down on disappear. If a fresh session is allocated on each render, that teardown-and-rebuild cycle prevents checkout from ever reaching `.ready`.

### UI not updating

**Problem**: Your custom slot content does not reflect changes such as validation errors, surcharge updates, or the loading state.

**Solution**: The Primer composable views observe their session's `@Published` state automatically — you do not need a manual subscription. Drive your custom UI by reading from the session state inside the slot closure:

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

Because each slot closure receives the observed session directly, capturing it in an external `@State` or copying its values into local variables breaks observation — always read `session.state` at the point of use.

## Getting help

If you encounter an issue not covered here:

1. Capture the `diagnosticsId` from the `PrimerError` delivered in `onCompletion` (SwiftUI) or `primerCheckoutPresenterDidFailWithError(_:checkoutData:)` (UIKit).
2. Verify your configuration in the [Primer Dashboard](https://dashboard.primer.io).
3. Contact Primer support and include the `diagnosticsId` so the team can trace your request.

```swift theme={"dark"}
.primerCheckoutSession(session) { state in
    if case let .failure(error, _) = state {
        // Share this value with Primer support
        print("Diagnostics ID: \(error.diagnosticsId)")
    }
}
```

## See also

> [card] [Installation](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/installation.md)
>
> Add the SDK via SPM or CocoaPods

> [card] [Handle Payment Result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md)
>
> Process success, failure, and dismissal

> [card] [Primer Error](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error.md)
>
> Inspect errors and the diagnosticsId

> [card] [Checkout Session](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session.md)
>
> Own and observe the session lifecycle


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