---
id: "tools/customer-center/customer-center-integration-ios"
title: "Integrating Customer Center on iOS"
description: "Installation"
permalink: "/docs/tools/customer-center/customer-center-integration-ios"
slug: "customer-center-integration-ios"
version: "current"
original_source: "docs/tools/customer-center/customer-center-integration-ios.mdx"
---

> **AI agents:** This is the Markdown version of a RevenueCat documentation page. For the complete documentation index, see [llms.txt](https://www.revenuecat.com/docs/llms.txt).

## Installation

[![Release](https://img.shields.io/github/release/RevenueCat/purchases-ios.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-ios/releases)

Before integrating the Customer Center in iOS, you need to add the RevenueCatUI SDK 5.14.0 or higher to your app.

### Using SPM

#### If you already have `RevenueCat` in your project:

1. Open your project settings and select "Package Dependencies":

![Change version](https://github.com/RevenueCat/purchases-ios/assets/685609/d317fd33-8270-4d9b-9b38-8f5f14342b04)

2. Double-click and make sure version is at least 5.14.0:

![Configure version](https://github.com/RevenueCat/purchases-ios/assets/685609/f537a1e1-a1ab-4e6f-986c-05abdcf1dd9f)

3. Open your target settings and find "Frameworks, Libraries, and Embedded Content":

![Find frameworks in your target](https://github.com/RevenueCat/purchases-ios/assets/685609/af078e9a-4b98-42c6-8ca7-6f79aebdf3e0)

4. Add `RevenueCatUI`:

![Add RevenueCatUI dependency](https://github.com/RevenueCat/purchases-ios/assets/685609/c2a3498c-b80d-405c-bdf6-75de927ea58e)

#### First time integrating the RevenueCat SDK:

1. Click File -> Add Packages...

2. Search for `git@github.com:RevenueCat/purchases-ios.git` and make sure version is at least 5.14.0:

![Adding purchases-ios dependency](https://github.com/RevenueCat/purchases-ios/assets/685609/18291043-5710-4944-ba12-7d6d83bde240)

3. Add `RevenueCat` and `RevenueCatUI` SPM dependency to your project:

![Add paywall](https://www.revenuecat.com/docs_images/customer-center/9140485-Screenshot_2023-08-04_at_12.08.07_78343500e565bc1fd0fceaac72486876.png)

### Using CocoaPods

Add the following to your `Podfile`:

```ruby
pod 'RevenueCat'
pod 'RevenueCatUI'
```

## Integration

The CustomerCenterView can be integrated in a few different ways depending on how you want to present it within your app.

The simplest way to present the CustomerCenterView is using a modal sheet:

```swift
import RevenueCatUI
...

var body: some View {
    Group {
        NavigationStack {
            HomeView()
                .navigationTitle("Home")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .topBarLeading) {
                        Button {
                        } label: {
                            Image(systemName: "line.3.horizontal")
                        }
                    }
                    ToolbarItem(placement: .topBarTrailing) {
                        Button {
                            self.isCustomerCenterPresented = true
                        } label: {
                            Image(systemName: "person.crop.circle")
                        }
                    }
                }
        }
    }
    .foregroundColor(.white)
    .sheet(isPresented: $isCustomerCenterPresented) {
        CustomerCenterView()
    }
}
```

If you prefer a more declarative and reusable approach, you can use the provided view modifier:

```swift
import RevenueCatUI
...

VStack {
    Button {
        self.presentingCustomerCenter = true
    } label: {
        TemplateLabel(name: "Customer Center", icon: "person.fill")
    }
}
.presentCustomerCenter(isPresented: self.$presentingCustomerCenter) {
    self.presentingCustomerCenter = false
}
```

For apps already using NavigationStack or NavigationView, CustomerCenterView can be pushed onto your stack:

```swift
import RevenueCatUI
...

var body: some View {
    NavigationStack {
        HomeView()
            .navigationTitle("Home")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button {
                    } label: {
                        Image(systemName: "line.3.horizontal")
                    }
                }
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        self.isCustomerCenterPresented = true
                    } label: {
                        Image(systemName: "person.crop.circle")
                    }
                }
            }
    }
    .navigationDestination(
        isPresented: isPresented
    ) {
        CustomerCenterView(
            usesNavigationStack: true, // set this to false when using a NavigationView
            usesExistingNavigation: true,
            shouldShowCloseButton: false
        )
    }
    .foregroundColor(.white)
    .sheet(isPresented: $isCustomerCenterPresented) {
        CustomerCenterView()
    }
}
```

### Listening to events

We've added a way to listen to events that occur within the `CustomerCenterView`:

```swift
CustomerCenterView()
    .onCustomerCenterRestoreInitiated { resume in
        // TODO: Perform any gating logic before restore begins
        resume()
    }
    .onCustomerCenterRestoreStarted {
        // TODO: Handle restore started
    }
    .onCustomerCenterRestoreFailed { error in
        // TODO: Handle error
    }
    .onCustomerCenterRestoreCompleted { customerInfo in
        // TODO: Handle restore completed
    }
    .onCustomerCenterShowingManageSubscriptions {
        // TODO: Handle showing manage subscriptions
    }
    .onCustomerCenterRefundRequestStarted { productId in
        // TODO: Handle refund request started
    }
    .onCustomerCenterRefundRequestCompleted { productId, status in
        // TODO: Handle refund request completed
    }
    .onCustomerCenterFeedbackSurveyCompleted { optionId in
        // TODO: Handle feedback survey completed
    }
    .onCustomerCenterManagementOptionSelected { managementOption in
        // TODO: Handle Customer Center management option selected
    }
    .onCustomerCenterCustomActionSelected { actionIdentifier, purchaseIdentifier in
        // TODO: Handle custom action selected
    }
```

### Intercepting an initiated restore

You can intercept a restore flow before it begins by using the `onCustomerCenterRestoreInitiated` view modifier in SwiftUI. If you are presenting Customer Center with UIKit, you can also implement `customerCenterViewController(_:didInitiateRestoreWith:)` on `CustomerCenterViewControllerDelegate`.

When a user initiates a restore, your interceptor callback will be invoked with a resume callback that you must call to either proceed with or cancel the restore.

The restore flow will pause until you call the `resume` action. If you call `resume(false)` or `resume(shouldProceed: false)`, the restore will be cancelled. If you call `resume(true)` or `resume()`, the restore will proceed as normal.

**iOS (SwiftUI)**

```swift
CustomerCenterView()
    .onCustomerCenterRestoreInitiated { resume in
        print("User is attempting to restore purchases")
        Task {
            do {
                try await performAuthentication()
                print("Authentication complete, presenting restore flow")
                resume()
            } catch {
                print("Authentication failed, cancel restore flow")
                resume(shouldProceed: false)
                presentErrorAlertForAuthentication()
            }
        }
    }
```

**iOS (UIKit)**

```swift
extension ViewController: CustomerCenterViewControllerDelegate {

    func customerCenterViewController(_ controller: CustomerCenterViewController,
                                      didInitiateRestoreWith resume: @escaping (Bool) -> Void) {
        Task {
            do {
                try await performAuthentication()
                print("Authentication complete, presenting restore flow")
                resume(true)
            } catch {
                print("Authentication failed, cancel restore flow")
                resume(false)
                presentErrorAlertForAuthentication()
            }
        }
    }

}
```

### Custom Actions

:::info\[Custom Actions support]
The minimum supported version is iOS SDK version 5.34.0.
:::

Custom Actions allow you to add your own custom management options to the Customer Center. When a customer taps on a custom action, your app receives a callback with the custom action identifier, allowing you to execute your own code.

To handle custom actions, use the `.onCustomerCenterCustomActionSelected` modifier:

```swift
CustomerCenterView()
.onCustomerCenterCustomActionSelected { actionIdentifier, purchaseIdentifier in
    // Handle your custom action
    // Execute your custom logic here
}
```

Custom actions are configured in the RevenueCat dashboard under Customer Center settings, where you can:

- Set a custom identifier for the action
- Configure the display text and localization
- Position the action within the management options

## Setup promotional offers

Promotional Offers allow developers to apply custom pricing and trials to new customers and to existing and lapsed subscriptions. Unique promotional offers can be assigned to different paths and survey responses in the Customer Center, but first they must be set up in App Store Connect.

The Customer Center will automatically show offers based on specific user actions. By default we have defined it for cancellations but it can be modified to any of the defined paths. Refer to [configuring App Store Connect promotional offers](https://www.revenuecat.com/docs/tools/customer-center/customer-center-promo-offers-apple) for detailed instructions.

Learn more about configuring the Customer Center in the [configuration guide](https://www.revenuecat.com/docs/tools/customer-center/customer-center-configuration).
