# Embed the Preference Center

Create custom Preference Center UIs by fetching the config and building your own subscription management interface.

This guide covers creating custom Preference Center UIs for Unity applications. Unlike the default Preference Center, you build your own UI from scratch using the Preference Center configuration and subscription list APIs.

## Override Default Display Behavior

By default, the plugin shows the out-of-the-box Preference Center UI. To provide your own UI, disable the default for a given Preference Center and handle the display request in the `OnPreferenceCenterDisplay` event.

```csharp
Airship.Shared.preferenceCenter.SetAutoLaunchDefaultPreferenceCenter("preference-center-id", false);

Airship.Shared.OnPreferenceCenterDisplay += (string preferenceCenterId) => {
    // Display your custom Preference Center UI
};
```


## Fetching Preference Center Config

The Preference Center config contains all the information needed to build your UI, including subscription lists, sections, and display settings. `GetConfig` is asynchronous, so run it as a coroutine.

```csharp
StartCoroutine(Airship.Shared.preferenceCenter.GetConfig("preference-center-id",
    (PreferenceCenterConfig config) => {
        // Build your UI from the config
    },
    (System.Exception error) => {
        Debug.LogError("Failed to load Preference Center config: " + error.Message);
    }
));
```


> **Note:** The config might not be available immediately on first app start. Implement exponential backoff if automatically retrying, or provide a UI for users to manually retry.


## Config Structure

`PreferenceCenterConfig` mirrors the Preference Center form JSON. A config has an `id`, a `display`, and a list of `sections`. Each section has a list of `items`.

Most members are lowercase fields, but members that are snake_case on the wire, along with the `type` discriminators, are exposed as PascalCase read-only properties. Use `item.SubscriptionId` rather than `item.subscriptionId`, and `section.Type` rather than `section.type`.

**PreferenceCenterConfig**

| Member | Type | Description |
| :----- | :--- | :---------- |
| `id` | `string` | The Preference Center ID. |
| `display` | `PreferenceCenterCommonDisplay` | Title and description for the Preference Center. |
| `sections` | `List<PreferenceCenterSection>` | The sections to render. |

**PreferenceCenterCommonDisplay**

| Member | Type | Description |
| :----- | :--- | :---------- |
| `name` | `string` | The display name. |
| `description` | `string` | Optional description. |
| `icon` | `string` | Optional icon URL. Set on alert items only. |

**PreferenceCenterSection**

| Member | Type | Description |
| :----- | :--- | :---------- |
| `Type` | `PreferenceCenterSectionType` | `Section`, `LabeledSectionBreak`, or `Unknown`. |
| `RawType` | `string` | The raw type string, for types this plugin version does not model. |
| `id` | `string` | The section ID. |
| `display` | `PreferenceCenterCommonDisplay` | Section heading. |
| `items` | `List<PreferenceCenterItem>` | The items in the section. A labeled section break is a heading with no items. |
| `conditions` | `List<PreferenceCenterCondition>` | Display conditions for the section. |

**PreferenceCenterItem**

| Member | Type | Description |
| :----- | :--- | :---------- |
| `Type` | `PreferenceCenterItemType` | `ChannelSubscription`, `ContactSubscription`, `ContactSubscriptionGroup`, `Alert`, or `Unknown`. |
| `RawType` | `string` | The raw type string, for types this plugin version does not model. |
| `id` | `string` | The item ID. |
| `display` | `PreferenceCenterCommonDisplay` | Item label and description. |
| `SubscriptionId` | `string` | The subscription list ID. Subscription items only. |
| `scopes` | `List<string>` | Subscription scopes. Contact subscription items only. |
| `components` | `List<PreferenceCenterContactSubscriptionGroupItemComponent>` | Group components, each with its own `scopes` and `display`. Contact subscription group items only. |
| `button` | `PreferenceCenterAlertItemButton` | The alert button, with `text` and `ContentDescription`. Alert items only. |
| `conditions` | `List<PreferenceCenterCondition>` | Display conditions for the item. |

**PreferenceCenterCondition**

| Member | Type | Description |
| :----- | :--- | :---------- |
| `Type` | `PreferenceCenterConditionType` | `NotificationOptIn` or `Unknown`. |
| `WhenStatus` | `PreferenceCenterOptInStatus` | `OptIn`, `OptOut`, or `Unknown`. Show the section or item only when the user's notification opt-in state matches. |

Switch on `Type` rather than assuming a shape, and handle the `Unknown` case. Section and item types added to the Preference Center after your plugin version was built deserialize as `Unknown` instead of throwing, so a config using a newer type still loads and the members specific to that type are left unset.

> **Note:** An alert button's `actions` object is not surfaced in Unity, because `JsonUtility` cannot represent arbitrary JSON.


## Building Your Custom UI

You need to:

1. Fetch the config to get the list of subscription lists and their current state.
2. Build your UI using the config data (sections, subscription lists, display settings).
3. Update subscription lists when users make changes using the [Subscription List APIs](https://www.airship.com/docs/developer/sdk-integration/unity/audience/subscription-lists/).

The config describes what to render, but it does not carry the user's current subscription state. Fetch that separately with `GetSubscriptionLists` on the channel or the contact, and use it to set the initial state of your toggles.

### Example

This example walks the config and logs each subscription item. Replace the logging with your own UI construction.

```csharp
using AirshipSDK;

StartCoroutine(Airship.Shared.preferenceCenter.GetConfig("preference-center-id",
    (PreferenceCenterConfig config) => {
        Debug.Log(config.display.name);

        foreach (PreferenceCenterSection section in config.sections) {
            if (section.Type == PreferenceCenterSectionType.LabeledSectionBreak) {
                // A heading with no items
                Debug.Log("-- " + section.display.name + " --");
                continue;
            }

            Debug.Log(section.display.name);

            foreach (PreferenceCenterItem item in section.items) {
                switch (item.Type) {
                    case PreferenceCenterItemType.ChannelSubscription:
                        Debug.Log(item.display.name + " -> " + item.SubscriptionId);
                        break;
                    case PreferenceCenterItemType.ContactSubscription:
                        Debug.Log(item.display.name + " -> " + item.SubscriptionId
                            + " " + string.Join(", ", item.scopes));
                        break;
                    case PreferenceCenterItemType.ContactSubscriptionGroup:
                        foreach (var component in item.components) {
                            Debug.Log(component.display.name + " "
                                + string.Join(", ", component.scopes));
                        }
                        break;
                    case PreferenceCenterItemType.Alert:
                        Debug.Log(item.display.name + " [" + item.button.text + "]");
                        break;
                }
            }
        }
    }
));
```


Use the [Subscription List APIs](https://www.airship.com/docs/developer/sdk-integration/unity/audience/subscription-lists/) to persist changes. Channel subscription items map to the channel subscription list APIs, and contact subscription and contact subscription group items map to the scoped contact subscription list APIs.
