Audience Management

Airship provides several ways to segment and personalize your audience through tags, attributes, events, and subscription lists. For more in-depth overview on each of those concepts, see Audience User Guide and Personalization User Guide.

This guide will cover how to link channels to a user as well as set segmentation and personalization data at both the user and device level.

Channels

Each device/app install will generate a unique identifier known as the Channel ID. Once a Channel ID is created, it will persist in the application until the app is reinstalled, or has its internal data is cleared.

Accessing the Airship Channel ID

Apps can access the Channel ID directly through the SDK.

Accessing the channel ID

val channelId = UAirship.shared().channel.id
String channelId = UAirship.shared().getChannel().getId();
let channelID = Airship.channel.identifier
NSString *channelID = UAirship.channel.identifier;
const channelId = await Airship.channel.getChannelId();
String channelId = await Airship.channel.identifier;
Airship.channel.getChannelId((channelID) => {
    console.log("Channel: " + channelID)
})
const channelID = await Airship.channel.getChannelId()
using AirshipDotNet;

string channelId = Airship.Instance.ChannelId;
using UrbanAirship.NETStandard;

string channelId = Airship.Instance.ChannelId;
var channelId = Airship.channel.identifier;
string channelId = UAirship.Shared.ChannelId;

The Channel ID is asynchronously created, so it may not be available right away on the first run. Changes to Channel data will automatically be batched and applied when the Channel is created, so there is no need to wait for the Channel to be available before modifying any data.

Applications that need to access the Channel ID can use a listener to be notified when it is available.

Listening for the Channel ID

// Using the channelIdFlow
scope.launch {
    UAirship.shared().channel.channelIdFlow.collect { channelId ->
        Log.d("Sample", "Channel created: $channelId")
    }
}

// Using the AirshipChannelListener
UAirship.shared().channel.addChannelListener(object : AirshipChannelListener {
    override fun onChannelCreated(channelId: String) {
        Log.d("Sample", "Channel created: $channelId")
    }
})
UAirship.shared().getChannel().addChannelListener(new AirshipChannelListener() {
    @Override
    public void onChannelCreated(@NonNull String channelId) {
        // created
    }
});
NotificationCenter.default.addObserver(
    self,
    selector: #selector(refreshView),
    name: AirshipNotifications.ChannelCreated.name,
    object: nil
)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshView) name:UAirshipNotificationChannelCreated.name object:nil];
Airship.addListener(EventType.ChannelCreated, (event) => {
    console.log('Channel created: ', event.channelId);
});
Airship.channel.onChannelCreated.listen((event) {
    debugPrint('Channel created $event');
});
Airship.channel.onChannelCreated((event) => {
    console.log('Channel created: ' + event.channelId);
});
await Airship.channel.onChannelCreated(event => {
    console.log('Channel created: ' + event.channelId);
});
using AirshipDotNet;

static void OnChannelCreation(object sender, ChannelEventArgs args) {
    Console.WriteLine("Channel created: " + args.ChannelId);
}

Airship.Instance.OnChannelCreation += OnChannelCreation;
using UrbanAirship.NETStandard;

static void OnChannelCreation(object sender, ChannelEventArgs args) {
    Console.WriteLine("Channel created: " + args.ChannelId);
}

Airship.Instance.OnChannelCreation += OnChannelCreation;
Airship.addEventListener(Airship.Airship.eventChannelCreated, function(e) {
    Ti.API.info('Channel created' + e.channelId);
});
UAirship.Shared.OnChannelUpdated += (string channelId) => {
    Debug.Log ("Channel updated: " + channelId);
};

Channel Capture tool

The Channel ID is valuable for troubleshooting individual device issues in production apps. The Channel Capture tool can be used to expose the Channel ID to the user, who can then send it to Support.

If the Channel Capture tool is enabled, it will listen for six app foregrounds within 30 seconds. On the sixth open, the Channel ID will be copied to the user’s clipboard with a leading ua:. Paste your Channel ID from the clipboard to your preferred document. If there is no channel, only the ua: will be present. The Channel ID will remain on the clipboard for 60 seconds on iOS and until cleared on Android.

The Channel Capture tool can be disabled through the Airship Config options.

Disabling Channel Capture tool

val options = AirshipConfigOptions.newBuilder()
    // ...
    .setChannelCaptureEnabled(false)
    .build()
AirshipConfigOptions options = AirshipConfigOptions.newBuilder()
    // ...
    .setChannelCaptureEnabled(false)
    .build();
config.isChannelCaptureEnabled = false
config.isChannelCaptureEnabled = NO;
await Airship.takeoff({
    ...
    isChannelCaptureEnabled: false,
})
  Airship.takeOff(
    AirshipConfig(
      ...
      isChannelCaptureEnabled: false
    )
  );
Airship.takeoff({
    ...
    isChannelCaptureEnabled: false,
})
await Airship.takeoff({
    ...
    isChannelCaptureEnabled: false,
})
// Not supported
// Not supported
// Not supported
// Not supported

Delaying channel creation

Airship creates the channel if at least one feature is enabled in the Privacy Manager. To delay channel creation, use the Privacy Manager to disable all features during takeOff.

For more info on Privacy Manager, see: Data Collection.

Contacts

A Contact allows you to associate multiple devices to a single user or profile that may be associated with more than one device, such as a user’s tablet and phone. A device can only be associated with a single Contact.

A Contact can be either anonymous or named:

Anonymous Contacts still allow tags, attributes, subscription lists, and channel association.

When a Contact goes from anonymous to named, the anonymous data will automatically carry over to the Named User ID, if the Named User is new. If the Named User already exists, a conflict event will be emitted by the SDK and the app is responsible for migrating anonymous data to the existing Named User.

Managing the Contact’s identifier (Named User ID)

 Note

By default, Contacts can only be associated server-side, via the Named User API. In order to identify a Contact via the SDK, you must change the application’s Named Users security setting to allow Named Users to be set from devices. See: Client-Side Named User Association.

Identify can be called multiple times with the same Named User ID. The SDK will automatically deduplicate identify calls made with the same Named User ID. If the ID is changed from a previous value, the Contact will automatically be dissociated from the previous Named User ID.

Identifying the contact

UAirship.shared().contact.identify("some named user ID")
UAirship.shared().getContact().identify("some named user ID");
Airship.contact.identify("some named user ID")
[UAirship.contact identify:@"some named user ID"];
await Airship.contact.identify(namedUserId);
Airship.contact.identify(namedUserId);
Airship.contact.identify(namedUserId);
await Airship.contact.identify(namedUserId)
using AirshipDotNet;

Airship.Instance.IdentifyContact("some named user ID");
using UrbanAirship.NETStandard;

Airship.Instance.IdentifyContact("some named user ID");
Airship.contact.identify("some named user ID");
UAirship.Shared.NamedUserId = "some named user ID";

If the user logs out of the device, you may want to reset the contact. This will clear any anonymous data and dissociate the contact from the Named User ID, if set. This should only be called when the user manually logs out of the app, otherwise you will not be able to target the Channel by its Contact data.

Resetting the contact

UAirship.shared().contact.reset()
UAirship.shared().getContact().reset();
Airship.contact.reset()
[UAirship.contact reset];
await Airship.contact.reset();
Airship.contact.reset();
Airship.contact.reset();
await Airship.contact.reset()
using AirshipDotNet;

Airship.Instance.ResetContact();
using UrbanAirship.NETStandard;

Airship.Instance.ResetContact();
Airship.contact.reset();
UAirship.Shared.NamedUserId = null;

When you name a Contact, you are assigning a Named User ID to the contact. You can get the Named User ID only if you set it through the SDK.

Getting the Named User ID

UAirship.shared().contact.namedUserId
UAirship.shared().getContact().getNamedUserId();
await Airship.contact.namedUserID
[UAirship.contact getNamedUserIDWithCompletionHandler:^(NSString *namedUserID) {

}];
const namedUser = await Airship.contact.getNamedUserId();
await Airship.contact.namedUserId;
Airship.contact.getNamedUserId((namedUser) => {
    if (namedUser) {
        console.log("Named User ID: " + namedUser)
    }
});
const namedUser = await Airship.contact.getNamedUserId()
using AirshipDotNet;

Airship.Instance.NamedUser;
using UrbanAirship.NETStandard;

Airship.Instance.GetNamedUser(namedUser =>
{ 
    // Use the namedUser
});
Airship.contact.namedUserId;
UAirship.Shared.NamedUserId

Email channel association

When an email address is registered through the SDK, it will be registered for both transactional and commercial emails by default. To change this behavior, you can override the options to request Double Opt-InA process where users who sign up for messaging must confirm opting in before they can receive messages. for commercial messages.

Associating an email channel

val properties = JsonMap.newBuilder().put("place", "paris").build()
val options = EmailRegistrationOptions.commercialOptions(commercialDate, transactionalDate, properties)
UAirship.shared().contact.registerEmail("your@example.com", options)
JsonMap properties = JsonMap.newBuilder().put("place", "paris").build();
EmailRegistrationOptions options = EmailRegistrationOptions.commercialOptions(commercialDate, transactionalDate, properties);
UAirship.shared().getContact().registerEmail("your@example.com", options);
let options = EmailRegistrationOptions.commercialOptions(
    transactionalOptedIn: transactionalDate,
    commercialOptedIn: commercialDate,
    properties: properties
)
Airship.contact.registerEmail("your@example.com", options: options)
UAEmailRegistrationOptions* options = [UAEmailRegistrationOptions commercialOptionsWithTransactionalOptedIn:transactionalDate commercialOptedIn:commercialDate properties:properties];
[UAirship.contact registerEmail:@"your@example.com" options:options];
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported

SMS channel association

When an MSISDNThe mobile phone number of an individual in your Airship audience. Each MSISDN represents an individual mobile device. is registered through the SDK, Airship sends a message to that number, prompting them to opt in. For more information, see the SMS platform documentation: Non-Mobile Double Opt-In.

Associating an SMS channel

val options = SmsRegistrationOptions.options("senderId")
UAirship.shared().contact.registerSms("yourMsisdn", options)
SmsRegistrationOptions options = SmsRegistrationOptions.options("senderId");
UAirship.shared().getContact().registerSms("yourMsisdn", options);
let options = SMSRegistrationOptions.optIn(senderID: "senderId")
Airship.contact.registerSMS("yourMsisdn", options: options)
UASMSRegistrationOptions* options = [UASMSRegistrationOptions optInSenderID:@"senderId"];
[UAirship.contact registerSMS:"yourMsisdn" options:options];
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported

Open Channel association

Open Channels support notifications to any medium that can accept a JSON payload, through either the Airship API or web dashboard. For more information about Open Channels, see the Open Channels documentation.

Associating an Open Channel

val options = OpenChannelRegistrationOptions.options("platformName")
UAirship.shared().contact.registerOpenChannel("address", options)
OpenChannelRegistrationOptions options = OpenChannelRegistrationOptions.options("platformName");
UAirship.shared().getContact().registerOpenChannel("address", options);
let options = OpenRegistrationOptions.optIn(platformName: "platformName", identifiers: identifiers)
Airship.contact.registerOpen("address", options: options)
UAOpenRegistrationOptions* options = [UAOpenRegistrationOptions optInSenderID:@"platformName"];
[UAirship.contact registerOpen:"address" options:options];
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported
// Not supported

Tags

Tags are segmentation attributes that help you message users based on preferences, actions, or behaviors within your app. They can be set with client code within an app, or passed server-side based on CRM information. Additionally, Airship lets you set tags from your app when a user clicks on an element of a notification.

Tags are static, either on or off, and capture who a user is. You can build SegmentsA grouping of audience members selected by unique or shared identifiers. Multiple identifiers can be combined within a Segment. based on combinations of tags using boolean logic and send messages when tag addition or removal events occur.

Tags are binary, meaning there’s no functionality for adding values or properties, such as cumulative values for the same action occurring multiple times.

 Tip

Tags allow you to build campaigns around user interests and can be grouped to form reusable tag groups or segments. In addition to tagging for use of key features, think about the categories, divisions or departments your brand serves. For sports, this might be teams and players. For media, this might be breaking news, local news, or sports.

Device Tags

Device tags are tags managed on the Channel by the SDK. These tags can be modified or fetched from the Channel.

Modifying tags

UAirship.shared().channel.editTags()
    .addTag("some_tag")
    .removeTag("some_other_tag")
    .apply()
UAirship.shared().getChannel().editTags()
    .addTag("some_tag")
    .removeTag("some_other_tag")
    .apply();
Airship.channel.editTags { editor in
    editor.set(["one", "two", "three"])
    editor.add("a_tag")
    editor.remove("three")
}
[UAirship.channel editTags:^(UATagEditor *editor) {
    [editor setTags:@[@"one", @"two", @"three"]];
    [editor addTag:@"a_tag"];
    [editor removeTag:@"three"];
}];
Airship.channel.editTags()
    .addTags(["one", "two", "three"])
    .removeTags(["some_tag"])
    .apply()
Airship.channel.addTags(["flutter"]);
Airship.channel.removeTags(["some-tag"]);
Airship.channel.editTags()
    .addTags(["one", "two", "three"])
    .removeTags(["some_tag"])
    .apply()
Airship.channel.editTags()
    .addTags(["one", "two", "three"])
    .removeTags(["some_tag"])
    .apply()
using AirshipDotNet;

// Add tags
Airship.Instance.EditDeviceTags()
    .AddTags(new string[] { "one", "two", "three" })
    .Apply();

// Add a tag
Airship.Instance.EditDeviceTags()
    .AddTag("a_tag")
    .Apply();

// Remove a tag
Airship.Instance.EditDeviceTags()
    .RemoveTag("a_tag")
    .Apply();
using UrbanAirship.NETStandard;

// Add tags
Airship.Instance.EditDeviceTags()
    .AddTags(new string[] { "one", "two", "three" })
    .Apply();

// Add a tag
Airship.Instance.EditDeviceTags()
    .AddTag("a_tag")
    .Apply();

// Remove a tag
Airship.Instance.EditDeviceTags()
    .RemoveTag("a_tag")
    .Apply();
Airship.channel.tags = ["some-tag", "other-tag"];
// Add tag
UAirship.Shared.AddTag ("some-tag");

// Remove tag
UAirship.Shared.RemoveTag ("other-tag");

Since the device is managing the tags, they can be fetched from the SDK.

Accessing tags

Val tags = UAirship.shared().channel.tags
ArrayList<String> tags = UAirship.shared().channel.tags;
let tags = Airship.channel.tags
NSArray* tags = [[UAirship channel] tags];
const tags = await Airship.channel.getTags();
List<String> tags = await Airship.channel.tags;
Airship.channel.getTags((tags) => {
    console.log("Tags: " + tag)
});
const tags = await Airship.channel.getTags()
using AirshipDotNet;

IEnumerable<string> tags = Airship.Instance.Tags;
using UrbanAirship.NETStandard;

IEnumerable<string> tags = Airship.Instance.Tags;
var tags = Airship.tags;
IEnumerable<string> tags = UAirship.Shared.Tags;

Tag Groups

Tag groups are tags scoped within a group. Tag groups are supported at both the Channel and Contact level. Tag groups are only able to be modified from the SDK but not fetched. If you need to be able to fetch the tag groups, consider using subscription lists.

Modifying tag groups

// Channel Tag Group Example
UAirship.shared().channel.editTagGroups()
    .addTag("loyalty", "bronze-member")
    .removeTag("loyalty", "bronze-member")
    .setTag("games", "bingo")
    .apply()

// Contact Tag Group Example
UAirship.shared().contact.editTagGroups()
    .addTag("loyalty", "bronze-member")
    .removeTag("loyalty", "bronze-member")
    .setTag("games", "bingo")
    .apply()
// Channel Tag Group Example
UAirship.shared().getChannel().editTagGroups()
    .addTag("loyalty", "bronze-member")
    .removeTag("loyalty", "bronze-member")
    .setTag("games", "bingo")
    .apply();

// Contact Tag Group Example
UAirship.shared().getContact().editTagGroups()
    .addTag("loyalty", "bronze-member")
    .removeTag("loyalty", "bronze-member")
    .setTag("games", "bingo")
    .apply();
// Channel Tag Group Example
Airship.channel.editTagGroups { editor in
    editor.add(["silver-member", "gold-member"], group:"loyalty")
    editor.remove(["bronze-member", "club-member"], group:"loyalty")
    editor.set(["bingo"], group:"games")
}

// Contact Tag Group Example
Airship.contact.editTagGroups { editor in
    editor.add(["silver-member", "gold-member"], group:"loyalty")
    editor.remove(["bronze-member", "club-member"], group:"loyalty")
    editor.set(["bingo"], group:"games")
}
// Channel Tag Group Example
[UAirship.channel editTagGroups:^(UATagGroupsEditor *editor) {
    [editor addTags:@[@"silver-member", @"gold-member"] group:@"loyalty"];
    [editor removeTags:@[@"bronze-member", @"club-member"] group:@"loyalty"];
    [editor setTags:@[@"bingo"] group:@"games"];
}];

// Contact Tag Group Example
[UAirship.contact editTagGroups:^(UATagGroupsEditor *editor) {
    [editor addTags:@[@"silver-member", @"gold-member"] group:@"loyalty"];
    [editor removeTags:@[@"bronze-member", @"club-member"] group:@"loyalty"];
    [editor setTags:@[@"bingo"] group:@"games"];
}];
// Channel
Airship.channel.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()

// Contact
Airship.contact.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()
// Channel Tag Group Example
Airship.channel.editTagGroups()
    ..addTags("loyalty", ["silver-member"])
    ..removeTags("loyalty", ["bronze-member"])
    ..apply()

// Named User Tag Group Example
Airship.contact.editTagGroups()
    ..addTags("loyalty", ["silver-member"])
    ..removeTags("loyalty", ["bronze-member"])
    ..apply()
// Channel Tag Group Example
Airship.channel.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()

// Named User Tag Group Example
Airship.contact.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()
// Channel Tag Group Example
Airship.channel.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()

// Named User Tag Group Example
Airship.contact.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()
using AirshipDotNet;

// Channel Tag Group Example
Airship.Instance.EditChannelTagGroups()
    .AddTags(new string[] { "silver-member", "gold-member" }, "loyalty")
    .SetTags(new string[] { "bingo" }, "games")
    .RemoveTags(new string[] { "bronze-member", "club-member"}, "loyalty")
    .Apply();


// Named User Tag Group Example
Airship.Instance.EditNamedUserTagGroups()
    .AddTags(new string[] { "silver-member", "gold-member" }, "loyalty")
    .SetTags(new string[] { "bingo" }, "games")
    .RemoveTags(new string[] { "bronze-member", "club-member" }, "loyalty")
    .Apply();
using UrbanAirship.NETStandard;

// Channel Tag Group Example
Airship.Instance.EditChannelTagGroups()
    .AddTags(new string[] { "silver-member", "gold-member" }, "loyalty")
    .SetTags(new string[] { "bingo" }, "games")
    .RemoveTags(new string[] { "bronze-member", "club-member"}, "loyalty")
    .Apply();


// Named User Tag Group Example
Airship.Instance.EditNamedUserTagGroups()
    .AddTags(new string[] { "silver-member", "gold-member" }, "loyalty")
    .SetTags(new string[] { "bingo" }, "games")
    .RemoveTags(new string[] { "bronze-member", "club-member" }, "loyalty")
    .Apply();
// Channel Tag Group Example
Airship.channel.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()

// Set tags on a group
Airship.contact.editTagGroups()
    .addTags("loyalty", ["silver-member"])
    .removeTags("loyalty", ["bronze-member"])
    .apply()
// Channel Tag Group Example
UAirship.Shared.EditChannelTagGroups ()
    .AddTag ("loyalty", "silver-member")
    .RemoveTag ("loyalty", "bronze-member")
    .Apply ();

// Named User Tag Group Example
UAirship.Shared.EditNamedUserTagGroups ()
    .AddTag ("loyalty", "silver-member")
    .RemoveTag ("loyalty", "bronze-member")
    .Apply ();

Testing Tags

After integrating tags into your project, you’ll want to make sure that you can send a message to the devices that have that tag.

In the dashboard, select Target Specific Users in the Audience step of a composer:

You can also create a Segment to filter audience members entering SequencesA series of messages that is initiated by a trigger. Airship sends messages in the series based on your timing settings, and you can also set conditions that determine its audience and continuation. Sequences can be connected to each other and to other messaging components to create continuous user experiences in a Journey.. See Configuring a Sequence trigger.

In the API, target specific users via the "audience" object. See API: Audience Selection.

You can target users who do or do not have a specific tag, either individually or as part of a SegmentA grouping of audience members selected by unique or shared identifiers. Multiple identifiers can be combined within a Segment..

Attributes

Attributes are key-value pairs, associated with either the channel or contact.

Modifying attributes

// Channel Attributes Example
UAirship.shared().channel.editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply()

// Contact Attributes Example
UAirship.shared().contact.editAttributes()
    .setAttribute("first_name", "Bobby")
    .setAttribute("birthday", Date(524300400000))
    .apply()
// Channel Attributes Example
UAirship.shared().getChannel().editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply();

// Contact Attributes Example
UAirship.shared().getContact().editAttributes()
    .setAttribute("first_name", "Bobby")
    .setAttribute("birthday", Date(524300400000))
    .apply();
// Channel Attributes Example
Airship.channel.editAttributes { editor in
    editor.set(string: "Bobby's Phone", attribute: "device_name")
    editor.set(number: 4.99, attribute: "average_rating")
    editor.remove("connection_type")
}

// Contact Attributes Example
Airship.contact.editAttributes { editor in
    editor.set(string: "Bobby", attribute: "first_name")
}
// Channel Attributes Example
[UAirship.channel editAttributes:^(UAAttributesEditor * editor) {
    [editor setString:@"Bobby's Phone" attribute:@"device_name"];
    [editor setNumber:@(4.99) attribute:@"average_rating"];
    [editor removeAttribute:@"connection_type"];
}];

// Contact Attributes Example
[UAirship.contact editAttributes:^(UAAttributesEditor * editor) {
    [editor setString:@"Bobby" attribute:@"first_name"];
}];
// Channel Attributes Example
Airship.channel.editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply();

// Contact Attributes Example
Airship.contact.editAttributes()
    .setAttribute("first_name", "Bobby")
    .apply();
// Channel Attributes Example
Airship.channel.editAttributes()
    ..setAttribute("device_name", "Bobby's Phone")
    ..setAttribute("average_rating", 4.99)
    ..removeAttribute("connection_type")
    ..apply()

// Contact Attributes Example
Airship.contact.editAttributes()
    ..setAttribute("first_name", "Bobby")
    ..apply()
// Channel Attributes Example
Airship.channel.editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply()

// Contact Attributes Example
Airship.contact.editAttributes()
    .setAttribute("first_name", "Bobby")
    .apply()
// Channel Attributes Example
Airship.channel.editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply()

// Contact Attributes Example
Airship.contact.editAttributes()
    .setAttribute("first_name", "Bobby")
    .apply()
using AirshipDotNet;

// Channel Attributes Example
Airship.Instance.EditChannelAttributes()
    .SetAttribute("device_name", "Bobby's Phone")
    .SetAttribute("average_rating", 4.99)
    .RemoveAttribute("connection_type")
    .Apply();

// Contact Attributes Example
Airship.Instance.EditNamedUserAttributes()
    .SetAttribute("first_name", "Bobby")
    .Apply();
using UrbanAirship.NETStandard;

// Channel Attributes Example
Airship.Instance.EditChannelAttributes()
    .SetAttribute("device_name", "Bobby's Phone")
    .SetAttribute("average_rating", 4.99)
    .RemoveAttribute("connection_type")
    .Apply();

// Contact Attributes Example
Airship.Instance.EditNamedUserAttributes()
    .SetAttribute("first_name", "Bobby")
    .Apply();
// Channel Attributes Example
Airship.channel.editAttributes()
    .setAttribute("device_name", "Bobby's Phone")
    .setAttribute("average_rating", 4.99)
    .removeAttribute("connection_type")
    .apply()

// Contact Attributes Example
Airship.contact.editAttributes()
    .setAttribute("first_name", "Bobby")
    .apply()
// Channel Attributes Example
UAirship.Shared.EditChannelAttributes()
    .SettAttribute("device_name", "Bobby's Phone")
    .SetAttribute("average_rating", 4.99)
    .RemoveAttribute("connection_type")
    .Apply()

// Contact Attributes Example
UAirship.Shared.EditNamedUserAttributes()
    .SettAttribute("first_name", "Bobby")
    .Apply()

Subscription lists

Subscription lists allow a Channel or Contact to subscribe to topic-based messaging.

Channel subscriptions apply only to the single channel.

Modifying Channel subscription lists

UAirship.shared().channel.editSubscriptionLists()
    .subscribe("food")
    .unsubscribe("sports")
    .apply()
UAirship.shared().getChannel().editSubscriptionLists();
    .subscribe("food");
    .unsubscribe("sports");
    .apply();
Airship.channel.editSubscriptionLists { editor in
    editor.subscribe("food")
    editor.unsubscribe("sports")
}
UASubscriptionListEditor *editor = [UAirship.channel editSubscriptionLists];
[editor subscribe:@"food"];
[editor unsubscribe:@"sports"];
[editor apply];
Airship.channel.editSubscriptionLists()
    .subscribe("food")
    .unsubscribe("sports")
    .apply()
Airship.channel.editSubscriptionLists()
    ..subscribe("food")
    ..unsubscribe("sports")
    ..apply();
Airship.channel.editSubscriptionLists()
    .subscribe("food")
    .unsubscribe("sports")
    .apply()
Airship.channel.editSubscriptionLists()
    .subscribe("food")
    .unsubscribe("sports")
    .apply()
// Not supported
using UrbanAirship.NETStandard;

Airship.Instance.EditChannelSubscriptionLists()
    .subscribe("food");
    .unsubscribe("sports");
    .apply();
// Not supported
// Not supported

Contact subscriptions are set at the user-level. When you modify a Contact subscription list, you must also include a Channel scope specifying the types that the subscription list applies to.

Modifying contact subscription lists

UAirship.shared().contact.editSubscriptionLists()
    .subscribe("food", "app")
    .unsubscribe("sports", "sms")
    .apply()
UAirship.shared().getContact().editSubscriptionLists();
    .subscribe("food", "app");
    .unsubscribe("sports", "sms");
    .apply();
Airship.contact.editSubscriptionLists { editor in
    editor.subscribe("food", scope: .app)
    editor.unsubscribe("sports", scope: .sms)
}
UAScopedSubscriptionListEditor *editor = [[UAContact shared] editSubscriptionLists];
[editor subscribe:"food" scope:"app"];
[editor unsubscribe:"sports" scope:"sms"];
[editor apply];
Airship.contact.editSubscriptionLists()
    .subscribe("food", SubscriptionScope.App)
    .unsubscribe("sports", SubscriptionScope.Sms)
    .apply()
Airship.contact.editSubscriptionLists()
    ..subscribe("food", ChannelScope.app)
    ..unsubscribe("sports", ChannelScope.sms)
    ..apply();
Airship.contact.editSubscriptionLists()
    .subscribe("food", "app")
    .unsubscribe("sports", "sms")
    .apply()
Airship.contact.editSubscriptionLists()
    .subscribe("food", "app")
    .unsubscribe("sports", "sms")
    .apply()
// not supported
using UrbanAirship.NETStandard;

Airship.Instance.EditContactSubscriptionLists()
    .subscribe("food", "app")
    .unsubscribe("sports", "sms")
    .apply()
// Not supported
// Not supported

Channel subscription lists are returned as a simple list of subscription IDs.

Fetching channel subscription lists

val subscriptions = UAirship.shared().channel.fetchSubscriptionLists()
PendingResult<Set<String>> subscriptionsPendingResult = 
    UAirship.shared().getChannel().fetchSubscriptionListsPendingResult();
let subscriptions = try await Airship.channel.fetchSubscriptionLists()
[[UAChannel shared] fetchSubscriptionListsWithCompletionHandler:^(NSArray<NSString *> * _Nullable channelSubscriptionLists, NSError * _Nullable error) {
    // Use the channelSubscriptionLists
}];
const subscriptionLists = await Airship.channel.getSubscriptionLists();
List<String> subscriptions = await Airship.channel.subscriptionLists
Airship.channel.getSubscriptionLists((lists) => {
console.log("subscriptions: " + lists)
});
const subscriptionLists = await Airship.channel.getSubscriptionLists()
// Not supported
// Not supported
// Not supported
// Not supported

Contact subscription lists are returned as a map of subscription list IDs to a list of platform scopes.

Fetching contact subscription lists

val subscriptions = UAirship.shared().contact.fetchSubscriptionLists()
PendingResult<Map<String, Set<Scope>>> subscriptionsPendingResult = 
    UAirship.shared().getContact().fetchSubscriptionListsPendingResult();
let subscriptions = try await Airship.contact.fetchSubscriptionLists()
[UAirship.contact fetchSubscriptionListsWithCompletionHandler:^(NSDictionary<NSString *,UAChannelScopes *> * _Nullable, NSError * _Nullable) {
    // Use the subscriptionLists
}];
const subscriptionLists = await Airship.contact.getSubscriptionLists();
Map<String, List<ChannelScope>> subscriptions = await Airship.contact.subscriptionLists
Airship.contact.getSubscriptionLists((lists) => {
    console.log("subscriptions: " + lists)
});
const lists = await Airship.contact.getSubscriptionLists() 
// Not supported
// Not supported
// Not supported
// Not supported