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

# PrimerCheckoutPresenter

> UIKit entry point for presenting the managed checkout and receiving delegate callbacks

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

`PrimerCheckoutPresenter` is the UIKit entry point for the managed checkout. It presents the
CheckoutComponents UI from a `UIViewController` and delivers results through a delegate, acting as a
bridge between UIKit apps and the underlying SwiftUI implementation.

> [!NOTE]
>
> For pure SwiftUI apps, use [`PrimerCheckout`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout)
> directly instead of this class. `PrimerCheckoutPresenter` exists for view-controller based
> navigation. Objective-C code can call `dismiss` and the two `presentCheckout` overloads that take
> no settings. `PrimerCheckoutPresenterDelegate` is a Swift protocol, so a Swift object must receive
> the results.

## Declaration

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

The presenter is a `@MainActor` singleton: call its static methods from the main thread (the
default for UIKit lifecycle callbacks). It is also exposed to Objective-C via `@objc`.

## Properties

| Property | Type | Description |
| - | - | - |
| `shared` | `PrimerCheckoutPresenter` | The shared singleton instance. Use it to assign the `delegate`. Exposed to Objective-C. |
| `delegate` | `PrimerCheckoutPresenterDelegate?` | The object that receives checkout result and lifecycle callbacks. Held weakly. |
| `isAvailable` | `Bool` | `static`. `true` when CheckoutComponents can be presented on the current OS version. |
| `isPresenting` | `Bool` | `static`. `true` while a checkout is being presented or is currently on screen. Check it before calling `presentCheckout`. |

```swift theme={"dark"}
@objc public static let shared = PrimerCheckoutPresenter()

public weak var delegate: PrimerCheckoutPresenterDelegate?

@objc public static var isAvailable: Bool
@objc public static var isPresenting: Bool
```

> [!NOTE]
>
> `delegate` is a `weak` reference. Retain the object you assign (for example the presenting view
> controller) for as long as checkout is on screen, otherwise callbacks will not fire.

## Presenting checkout

`presentCheckout` has five overloads. They differ only in how much you configure and whether you
supply the presenting view controller yourself. All overloads accept an optional trailing
`completion` closure that runs once the checkout UI has finished presenting. If the automatic
overloads cannot find a presenting view controller, `completion` does not run. While a previous
checkout is still animating in, the call presents nothing and `completion` runs immediately. A
checkout that is already on screen does not stop a new call. Check `isPresenting`, and do not call
`presentCheckout` while it is `true`.

`primerSettings` and `primerTheme` have no Swift defaults; `completion` and the two Express Checkout
shipping handlers default to `nil`. The overloads that omit settings or theme apply the SDK's
current global `PrimerSettings` and a default `PrimerCheckoutTheme()` internally. For the shipping
handlers, see
[Express Checkout shipping](https://primer.io/docs/checkout/primer-checkout/guides-and-recipes/express-checkout-shipping.md).

### From an explicit view controller

```swift theme={"dark"}
@objc public static func presentCheckout(
    clientToken: String,
    from viewController: UIViewController,
    completion: (() -> Void)? = nil
)

public static func presentCheckout(
    clientToken: String,
    from viewController: UIViewController,
    primerSettings: PrimerSettings,
    completion: (() -> Void)? = nil,
    onShippingAddressChange: ShippingAddressChangeHandler? = nil,
    onShippingOptionChange: ShippingOptionChangeHandler? = nil
)

public static func presentCheckout(
    clientToken: String,
    from viewController: UIViewController,
    primerSettings: PrimerSettings,
    primerTheme: PrimerCheckoutTheme,
    completion: (() -> Void)? = nil,
    onShippingAddressChange: ShippingAddressChangeHandler? = nil,
    onShippingOptionChange: ShippingOptionChangeHandler? = nil
)
```

### With automatic view controller detection

These overloads locate the top-most presenting view controller for you, which is convenient when
you do not have a view controller reference at hand.

```swift theme={"dark"}
@objc public static func presentCheckout(
    clientToken: String,
    completion: (() -> Void)? = nil
)

public static func presentCheckout(
    clientToken: String,
    primerSettings: PrimerSettings,
    completion: (() -> Void)? = nil,
    onShippingAddressChange: ShippingAddressChangeHandler? = nil,
    onShippingOptionChange: ShippingOptionChangeHandler? = nil
)
```

> [!WARNING]
>
> If no presenting view controller can be found, the automatic overloads report a
> [`PrimerError`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error) through
> `primerCheckoutPresenterDidFailWithError(_:checkoutData:)` and do not call `completion`. Assign the
> `delegate` before calling these methods.

### Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `clientToken` | `String` | — | The client token for the session. Generate it from your backend with the [client session](https://primer.io/docs/checkout/client-session.md) API. |
| `viewController` | `UIViewController` | — | The view controller to present from. Omitted in the automatic-detection overloads. |
| `primerSettings` | `PrimerSettings` | — | Configuration applied to this checkout session. Overloads that omit it use the SDK's current global `PrimerSettings`. See [`PrimerSettings`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-settings). |
| `primerTheme` | `PrimerCheckoutTheme` | — | Theme configuration for the [design tokens](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/theming/primer-theme.md). Overloads that omit it use a default `PrimerCheckoutTheme()`. |
| `completion` | `(() -> Void)?` | `nil` | Optional closure invoked once the checkout UI has finished presenting. If the automatic overloads find no presenting view controller, it is not called. While a previous checkout is still animating in, the call presents nothing and `completion` is invoked immediately. |
| `onShippingAddressChange` | `ShippingAddressChangeHandler?` | `nil` | Express Checkout: supplies the shipping options for the address the shopper entered in the wallet sheet. Available on the overloads that take `primerSettings`. See [Express Checkout shipping](https://primer.io/docs/checkout/primer-checkout/guides-and-recipes/express-checkout-shipping.md). |
| `onShippingOptionChange` | `ShippingOptionChangeHandler?` | `nil` | Express Checkout: commits the option the shopper selected, through your backend. Runs only when `onShippingAddressChange` is also set. Available on the overloads that take `primerSettings`. |

> [!NOTE]
>
> The overloads that take `primerSettings` or `primerTheme` are **not** `@objc` compatible because of
> those Swift-only parameter types. From Objective-C, you can call only
> `presentCheckout(clientToken:completion:)`, `presentCheckout(clientToken:from:completion:)` and
> `dismiss(animated:completion:)`. These two `presentCheckout` overloads use the SDK's global
> `PrimerSettings`. `PrimerCheckoutPresenterDelegate` is not exposed to Objective-C, so implement the
> delegate in Swift.

## Dismissing checkout

```swift theme={"dark"}
@objc public static func dismiss(
    animated: Bool = true,
    completion: (() -> Void)? = nil
)
```

| Parameter | Type | Default | Description |
| - | - | - | - |
| `animated` | `Bool` | `true` | Has no effect. The checkout always dismisses with animation. |
| `completion` | `(() -> Void)?` | `nil` | Optional closure invoked after dismissal completes. With no checkout on screen, it is invoked immediately. |

When a checkout is on screen, `dismiss` closes it. It then calls `primerCheckoutPresenterDidDismiss()`
on the delegate, unless that checkout already delivered its final result. With no checkout on
screen, `dismiss` does nothing and `completion` runs immediately.

The checkout also closes itself when a payment succeeds. It closes after the success screen
finishes, or right away when `uiOptions.isSuccessScreenEnabled` is `false`. Then it calls
`primerCheckoutPresenterDidCompleteWithSuccess(_:)`.

When a payment fails and `uiOptions.isErrorScreenEnabled` is `true` (the default), the delegate gets
`primerCheckoutPresenterDidFailWithError(_:checkoutData:)` right away. The sheet stays open on the
SDK error screen, so the shopper can retry or choose another payment method where available. When
`uiOptions.isErrorScreenEnabled` is `false`, the sheet closes on failure. The delegate then gets the
failure after the sheet has closed. See
[`PrimerSettings`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-settings).

### Swipe to dismiss

The checkout is shown as a sheet. When `uiOptions.dismissalMechanism` contains `.gestures` (the
default), the shopper can swipe it away. Without `.gestures`, the sheet is modal and shows no grabber.

The swipe is blocked while a payment is processing, so the sheet stays until the result is known.
What the delegate receives for a swipe depends on the screen:

| Screen when the shopper swipes | Delegate callback |
| - | - |
| Success screen | `primerCheckoutPresenterDidCompleteWithSuccess(_:)` |
| Error screen | `primerCheckoutPresenterDidDismiss()`. The failure was already reported when it happened. |
| Any other screen | `primerCheckoutPresenterDidDismiss()` |

## PrimerCheckoutPresenterDelegate

```swift theme={"dark"}
@available(iOS 15.0, *)
public protocol PrimerCheckoutPresenterDelegate: AnyObject
```

Conform to this protocol to receive checkout results. All three methods are required. A failed
3-D Secure challenge is reported through `primerCheckoutPresenterDidFailWithError(_:checkoutData:)`
like any other failure.

### Methods

| Method | Description |
| - | - |
| `primerCheckoutPresenterDidCompleteWithSuccess(_:)` | Called when payment is successful, with the [`PaymentResult`](#paymentresult). |
| `primerCheckoutPresenterDidFailWithError(_:checkoutData:)` | Called once for each failed attempt, including a failed initialization, with a [`PrimerError`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error). While the SDK error screen is enabled, the sheet stays open for a retry. This callback can then fire more than once, and a success or a dismissal can follow. `checkoutData` carries the payment id and order id when the payment was created before the failure, and is `nil` otherwise. See [`PrimerCheckoutState.failure`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state#failure-associated-values). |
| `primerCheckoutPresenterDidDismiss()` | Called when checkout is dismissed without completion. |

```swift theme={"dark"}
func primerCheckoutPresenterDidCompleteWithSuccess(_ result: PaymentResult)
func primerCheckoutPresenterDidFailWithError(_ error: PrimerError, checkoutData: PrimerCheckoutData?)
func primerCheckoutPresenterDidDismiss()
```

## PaymentResult

The success callback delivers a `PaymentResult` value.

```swift theme={"dark"}
public struct PaymentResult: Sendable, Equatable {
    public let paymentId: String
    public let status: PaymentStatus
    public let token: String?
    public let redirectUrl: String?
    public let errorMessage: String?
    public let amount: Int?
    public let currencyCode: String?
    public let paymentMethodType: String?
}
```

| Property | Type | Description |
| - | - | - |
| `paymentId` | `String` | The unique identifier of the payment. |
| `status` | `PaymentStatus` | The final status of the payment. |
| `token` | `String?` | The payment token, when available. |
| `redirectUrl` | `String?` | A redirect URL, when the flow involved one. |
| `errorMessage` | `String?` | A human-readable error message, when available. |
| `amount` | `Int?` | The payment amount in minor units. |
| `currencyCode` | `String?` | The ISO 4217 currency code. |
| `paymentMethodType` | `String?` | The payment method type used for the payment. |

### PaymentStatus

```swift theme={"dark"}
public enum PaymentStatus: Sendable {
    case pending
    case success
    case failed
}
```

| Case | Description |
| - | - |
| `pending` | The payment is still being processed. |
| `success` | The payment completed successfully. |
| `failed` | The payment failed. |

> [!TIP]
>
> When switching on `PaymentStatus`, always include a `default` case so your code stays
> forward-compatible with future status values.

## UIKit usage example

```swift theme={"dark"}
import PrimerSDK
import UIKit

final class CheckoutViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        PrimerCheckoutPresenter.shared.delegate = self
    }

    @IBAction func payButtonTapped(_ sender: UIButton) {
        PrimerCheckoutPresenter.presentCheckout(
            clientToken: clientToken,
            from: self
        )
    }
}

extension CheckoutViewController: PrimerCheckoutPresenterDelegate {
    func primerCheckoutPresenterDidCompleteWithSuccess(_ result: PaymentResult) {
        switch result.status {
        case .success:
            showReceipt(paymentId: result.paymentId)
        case .pending:
            showPendingState(paymentId: result.paymentId)
        case .failed:
            showError(message: result.errorMessage)
        default:
            break
        }
    }

    func primerCheckoutPresenterDidFailWithError(_ error: PrimerError, checkoutData: PrimerCheckoutData?) {
        showError(message: error.errorDescription, paymentReference: checkoutData?.payment?.id)
    }

    func primerCheckoutPresenterDidDismiss() {
        // The shopper closed checkout without completing a payment.
    }
}
```

To apply custom settings or theming, use the richer overload:

```swift theme={"dark"}
PrimerCheckoutPresenter.presentCheckout(
    clientToken: clientToken,
    from: self,
    primerSettings: settings,
    primerTheme: theme
)
```

> [!NOTE]
>
> `PrimerCheckoutPresenter.shared.delegate` is `weak`. Keep the conforming object (here, the view
> controller) alive for the duration of checkout so the callbacks are delivered.

## See also

> [card] [PrimerCheckout](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout.md)
>
> The SwiftUI view for embedding checkout directly in a SwiftUI hierarchy.

> [card] [PrimerCheckoutState](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state.md)
>
> The state model that drives the managed checkout flow.

> [card] [PrimerError](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error.md)
>
> The error type delivered to the failure callback.

> [card] [UIKit integration](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/uikit-integration.md)
>
> A guide to integrating the managed checkout in UIKit apps.


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