Preference Center

 Important

Airship preference centers are widgets that can be embedded in a page in an app or website. Please verify with your legal team that your full preference center page, including any web page for email preference centers, is compliant with local privacy regulations.

AirshipPreferenceCenter

Preference center is available in the PreferenceCenter module and subspec.

Importing the module

For CocoaPods, import using:

import AirshipKit
@import AirshipKit;

For everything else, use:

import AirshipPreferenceCenter
@import AirshipPreferenceCenter;

Displaying the Preference Center

The preference center can be displayed with a simple method call on the UAPreferenceCenter instance. By wiring this method call to a button in your app, you can quickly produce a user-initiated Preference Center with no additional effort.

Displaying the Preference Center
PreferenceCenter.shared.open("preferenceCenterID")
[[UAPreferenceCenter shared] openPreferenceCenter:@"preferenceCenterID"];
}

Adding a Custom Look and Feel

The default implementation displays with default settings for elements such as colors, fonts, and title text. In the following section we’ll explore ways to customize its look and feel without having to create custom UI.

Most developers will want to customize the look and feel to match their app’s existing style and layout. By setting a handful of top-level properties for the Preference Center you can override the default style to quickly arrive at a look and feel matching that of your app. We’ll cover a few of these to show how easy this process can be.

Styling the Preference Center

The preference center’s look can be customized by creating a UAPreferenceCenterStyle instance, setting its style properties, and then setting the style property of the Preference Center instance to the customized style instance.

For instance, we may want to change the color of the navigation bar in order to match our app and set custom fonts for the title and table view sections and preferences. In the example below, we assume the app has access to the “Roboto” font, but any stock or custom font may be used.

Styling the Preference Center
let style = PreferenceCenterStyle()

// Customize the style object
style.navigationBarColor = UIColor(red: 0.988, green: 0.694, blue: 0.106, alpha: 1)
style.titleColor = UIColor(red: 0.039, green: 0.341, blue: 0.490, alpha: 1)
style.tintColor = UIColor(red: 0.039, green: 0.341, blue: 0.490, alpha: 1)

style.titleFont = UIFont(name: "Roboto-Regular", size: 17.0)
style.sectionTextFont = UIFont(name: "Roboto-Bold", size: 14.0)
style.preferenceTextFont = UIFont(name: "Roboto-Light", size: 12.0)

// Set the style on the default Preference Center UI
PreferenceCenter.shared.style = style
UAPreferenceCenterStyle *style = [[UAPreferenceCenterStyle alloc] init];

// Customize the style object
style.navigationBarColor = [UIColor colorWithRed:0.988 green:0.694 blue:0.106 alpha:1];
style.titleColor = [UIColor colorWithRed:0.039 green:0.341 blue:0.490 alpha:1];
style.tintColor = [UIColor colorWithRed:0.039 green:0.341 blue:0.490 alpha:1];

style.titleFont = [UIFont fontWithName:@"Roboto-Regular" size:17.0];
style.sectionTextFont = [UIFont fontWithName:@"Roboto-Bold" size:13.0];
style.preferenceTextFont = [UIFont fontWithName:@"Roboto-Light" size:12.0];

// Set the style on the default Preference Center UI
[UAPreferenceCenter shared].style = style;

This can also be done without writing code by creating a plist file. Create a plist with the desired preference center style. All the keys correspond to properties on the UAPreferenceCenterStyle class.

Colors are represented by strings, either a valid color hexadecimal or a named color. Named color strings must correspond to a named color defined in a color asset within the main bundle.

Save the plist as AirshipPreferenceCenterStyle.plist file.

Styling the Preference Center using a plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>titleFont</key>
        <dict>
                <key>fontName</key>
                <string>Roboto-Regular</string>
                <key>fontSize</key>
                <string>17</string>
        </dict>
        <key>titleColor</key>
        <string>#00698f</string>
        <key>tintColor</key>
        <string>#00698f</string>
        <key>navigationBarColor</key>
        <string>myNavBarColor</string>
        <key>sectionTextFont</key>
        <dict>
                <key>fontName</key>
                <string>Roboto-Bold</string>
                <key>fontSize</key>
                <string>13</string>
        </dict>
        <key>preferenceTextFont</key>
        <dict>
                <key>fontName</key>
                <string>Roboto-Light</string>
                <key>fontSize</key>
                <string>17</string>
        </dict>
</dict>
</plist>

Overriding the Preference Center

If the provided preference center is insufficient for your app, you can build your own UI. To customize the preference center implementations you need to start by overriding the open behavior.

Override the open behavior

First, initialize the UAPreferenceCenterOpenDelegate with the custom Preference Center implementation.

PreferenceCenter.shared.openDelegate = self
[UAPreferenceCenter shared].openDelegate = self;

Then implement the openPreferenceCenter delegate.

func openPreferenceCenter(_ preferenceCenterID: String) -> Bool {
    /// implement custom preference center
    return true
}

- (BOOL)openPreferenceCenter:(NSString * _Nonnull)preferenceCenterID {
    // implement custom preference center
    return YES;
}

Building your own Preference Center UI

After overriding the open behavior, you have to implement your own UI. This example will help you to create your own custom peference center view.

First, let’s fetch the preference center configs.

Fetch the preference center config
PreferenceCenter.shared.config(preferenceCenterID: prefCenterId) { config in
    // Use the preference center config
}
[[UAPreferenceCenter shared] configForPreferenceCenterID:prefCenterId completionHandler:^(UAPreferenceCenterConfig * _Nullable config) {
    // Use the preference center config
}];

The PreferenceCenterConfig object includes the name and (optional) description for the Preference Center and a list of sections containing preference items.

Now, lets display the preference center items into your tableView.

// MARK: -
// MARK: UITableViewDataSource
    
func numberOfSections(in tableView: UITableView) -> Int {
    guard let sections = self.config?.sections.count else { return 0 }
    return sections
}
    
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let rows = self.config?.sections[section].items.count else { return 0 }
    return rows
}
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let defaultResult: () -> UITableViewCell =  {
        return tableView.dequeueReusableCell(withIdentifier: "PreferenceCenterCell", for: indexPath)
    }
        
    guard let item = self.config?.sections[indexPath.section].items[indexPath.row] else {
        return defaultResult()
    }
                               
    var cell: UITableViewCell?
    switch(item.itemType) {
    case .channelSubscription:
        guard let item = item as? ChannelSubscriptionItem
        else {
            return defaultResult()
        }
//      cell = self.bindChannelSubscriptionItem(item,
//                                              tableView: tableView,
//                                              indexPath: indexPath)
    case .alert:
        guard let item = item as? AlertItem else {
            return defaultResult()
        }
//      cell = self.bindAlertItem(item,
//                                tableView: tableView,
//                                indexPath: indexPath)
            
    case .contactSubscription:
        guard let item = item as? ContactSubscriptionItem
        else {
            return defaultResult()
        }
//      cell = self.bindContactSubscriptionItem(item,
//                                              tableView: tableView,
//                                              indexPath: indexPath)
    case .contactSubscriptionGroup:
        guard let item = item as? ContactSubscriptionGroupItem else {
            return defaultResult()
        }
//      cell = self.bindContactGroupSubscriptionItem(item,
//                                                   tableView: tableView,
//                                                   indexPath: indexPath)
    }

    return cell ?? defaultResult()
}

#pragma mark -
#pragma mark UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.config.sections.count;
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.config.sections[section].items.count;
}

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    
    UITableViewCell * (^defaultResult)(void) = ^{
        return [tableView dequeueReusableCellWithIdentifier:@"PreferenceCenterCell" forIndexPath:indexPath];
    };
    
    id<UAPreferenceItem> item = self.config.sections[indexPath.section].items[indexPath.row];
    if (!item) {
        return defaultResult();
    }
    
    UITableViewCell *cell = defaultResult();
    switch (item.itemType) {
        case UAPreferenceItemTypeChannelSubscription:
//            cell = [self bindChannelSubscriptionItem: (UAPreferenceChannelSubscriptionItem *)item tableView: tableView indexPath: indexPath];
            break;
        case UAPreferenceItemTypeAlert:
//            cell = [self bindAlertItem: (UAPreferenceAlertItem *)item tableView: tableView indexPath: indexPath];
            break;
        case UAPreferenceItemTypeContactSubscription:
//            cell = [self bindContactSubscriptionItem: (UAPreferenceContactSubscriptionItem *)item tableView: tableView indexPath: indexPath];
            break;
        case UAPreferenceItemTypeContactSubscriptionGroup:
//            cell = [self bindContactGroupSubscriptionItem: (UAPreferenceContactSubscriptionGroupItem *)item tableView: tableView indexPath: indexPath];
            break;
    }
    return cell;
}

If you want to fecth the set of subscriptions that the channel is currently subscribed to, then you have to use the fetchSubscriptionLists method from UAChannel. The IDs returned in the set correspond to Item IDs in the PreferenceCenterConfig object. An empty set indicates that the current channel is not subscribed to any lists.

Fetch the channel subscription lists
Channel.shared.fetchSubscriptionLists { channelSubscriptionLists, Error in
    // Use the channelSubscriptionLists
}
[[UAChannel shared] fetchSubscriptionListsWithCompletionHandler:^(NSArray<NSString *> * _Nullable channelSubscriptionLists, NSError * _Nullable error) {
        // Use the channelSubscriptionLists
}];

If you want to fetch the set of subscriptions that the contact is currently subscribed to, then you need to use The fetchSubscriptionLists method from UAContact. An empty set indicates that the current contact is not subscribed to any lists.

Fetch the contact subscription lists
Contact.shared.fetchSubscriptionLists { contactSubscriptionLists, error in
    // Use the contactSubscriptionLists
}
[[UAContact shared] fetchSubscriptionListsWithCompletionHandler:^(NSDictionary<NSString *,UAChannelScopes *> * _Nullable contactSubscriptionLists, NSError * error) {
    // Use the contactSubscriptionLists 
}];

Update the channel/contact subscription list

Once the preference config fetched and displayed, you can choose to subscribe/unsubscribe from a channel/contact subscription list.

To update a channel subscription lists when a preference item is updated, use the editSubscriptionLists method from UAChannel:

Update the channel subscription list
func onPreferenceChannelItemToggled(subscriptionItem: ChannelSubscriptionItem, isSubscribed:Bool){
    let editor = Channel.shared.editSubscriptionLists();
    if (isSubscribed) {
        editor.subscribe(subscriptionItem.subscriptionID)
    } else {
        editor.unsubscribe(subscriptionItem.subscriptionID)
    }
    editor.apply()
}
- (void)onPreferenceChannelItemToggled:(UAPreferenceChannelSubscriptionItem *)subscriptionItem isSubscribed:(BOOL)isSubscribed {
    UASubscriptionListEditor *editor = [[UAChannel shared] editSubscriptionLists];
    if (isSubscribed) {
        [editor subscribe:subscriptionItem.subscriptionID];
    } else {
        [editor unsubscribe:subscriptionItem.subscriptionID];
    }
    [editor apply];
}

To update a contact subscription lists when a contact preference item is updated, use the editSubscriptionLists method from UAContact:

Update the contact subscription list
func onPreferenceContactSubscriptionItemToggled(subscriptionItem: ContactSubscriptionItem, scopes:[ChannelScope], isSubscribed:Bool) {
    let editor = Contact.shared.editSubscriptionLists();
    if (isSubscribed) {
        scopes.forEach { editor.subscribe(subscriptionItem.subscriptionID, scope: $0) }
    } else {
        scopes.forEach { editor.unsubscribe(subscriptionItem.subscriptionID, scope: $0) }
    }
    editor.apply()
}
- (void)onPreferenceContactSubscriptionItemToggled:(UAPreferenceContactSubscriptionItem *)subscriptionItem scopes:(NSArray *)scopes isSubscribed:(BOOL)isSubscribed {
    UAScopedSubscriptionListEditor *editor = [[UAContact shared] editSubscriptionLists];
    if (isSubscribed) {
        for (id scope in scopes) {
            [editor subscribe:subscriptionItem.subscriptionID scope:(UAChannelScope)scope];
        }
    } else {
        for (id scope in scopes) {
            [editor unsubscribe:subscriptionItem.subscriptionID scope:(UAChannelScope)scope];
        }
    }
    [editor apply];
}

Deep linking

Deeplinking to a Preference Center is supported automatically within the SDK with URLs with uairship:// scheme. Preference Center deep links must include the ID of the Preference Center to be opened, in the form of uairship://preferences/prefCenterId.

To expose deep linking to a preference center externally, setup a deep link that calls through to PreferenceCenter.shared.open("prefCenterId").

func receivedDeepLink(_ url: URL, completionHandler: @escaping () -> ()) {
    // Parse the url and get the prefCenterId
    PreferenceCenter.shared.open("prefCenterId")
}
- (void)receivedDeepLink:(nonnull NSURL *)url completionHandler:(nonnull void (^)(void))completionHandler {
    
    // Parse the url and get the prefCenterId
    [[UAPreferenceCenter shared] openPreferenceCenter:prefCenterId];
}