> ## 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.1).
  The API is subject to change before the stable release.
</Warning>

## 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.1` 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.
</Note>

### 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](/docs/sdk/ios-checkout/v3.0.0-beta/installation) 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](/docs/checkout/client-session) belongs to the correct environment (sandbox vs. production). |

**Solution**: When initialization fails, the session delivers a `.failure(PrimerError)` through the `onCompletion` closure on `.primerCheckoutSession(_: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)")
    }
}
```

### Payment methods not showing

**Problem**: `PrimerPaymentMethods` renders its empty state with no payment methods listed.

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

**Solution**: Verify your [Primer Dashboard](https://dashboard.primer.io) configuration, and ensure the [client session](/docs/checkout/client-session) includes the correct `currencyCode` and `countryCode`. Payment methods are filtered server-side based on these values.

### 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`.
</Tip>

### 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). For UIKit hosts, track the challenge lifecycle through the optional `PrimerCheckoutPresenterDelegate` 3DS callbacks. See the [Handle payment result](/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result) 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(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(_: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`.
</Warning>

### 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(_:)` (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

<CardGroup cols={2}>
  <Card title="Installation" icon="download" href="/docs/sdk/ios-checkout/v3.0.0-beta/installation">
    Add the SDK via SPM or CocoaPods
  </Card>

  <Card title="Handle Payment Result" icon="circle-check" href="/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result">
    Process success, failure, and dismissal
  </Card>

  <Card title="Primer Error" icon="triangle-exclamation" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error">
    Inspect errors and the diagnosticsId
  </Card>

  <Card title="Checkout Session" icon="link" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session">
    Own and observe the session lifecycle
  </Card>
</CardGroup>
