iOS

This is a developers’ guide for setting up an Urban Airship configuration for iOS apps. We will take you through the basic technical steps for configuring Apple and UA services, show you how to send your first basic push notification and Message Center message, and provide detailed samples and instructions for using advanced Urban Airship features.

To see a fully-functioning iOS app demonstrating many of the Urban Airship features discussed in this guide, check out the samples packaged in the library on Bintray or GitHub: Swift Sample App or Objective-C Sample App.

Getting Started

There are a few important steps to get out of the way when integrating Urban Airship with your app for the first time. Follow these instructions carefully to ensure that you will have a smooth development experience.

Production vs Development Apps in Urban Airship

When you create or edit an application record on our server, you must select whether your app system is “In development, connecting to test servers,” or “In production, connecting to real push servers.” Apple treats the two servers separately, so a device token for development/sandbox will not work on production/distribution.

When building your app using a development provisioning profile, set your Urban Airship application to In development, and upload the development Push SSL certificate. To push to an app built with a distribution provisioning profile (either with a release build in Xcode, ad hoc distribution, or the iTunes App Store), use an application that is In production, and upload the production Push SSL certificate.

Because Apple treats development and production/distribution as completely separate instances, we suggest making two applications in the Urban Airship web interface. That way you can continue to build and develop your application even after releasing it without interrupting your users.

Warning

Do not a) submit to the App Store or b) test notifications on an ad hoc build while your app’s code is pointing to an Urban Airship app key that is set as In Development. Development apps use different tokens that, when included in a push to a production app, will fail and in many cases cause all other pushes to fail.

Always create a Production Status Urban Airship app first and make sure your application code is pointing to the production app key. For more tips on what to check before you release your app, see this support article.

APNS Setup

While you are setting up your development and production apps, you’ll want to configure them for push services. For detailed instructions on this process, please see the APNS Setup documentation

Urban Airship SDK Setup

Download and unzip the latest version of libUAirship into the same directory as your project. You should see a new directory, named Airship, containing the latest version of the libUAirship static library and headers, as well as an AirshipKit directory, containing an Xcode project that builds the AirshipKit embedded framework.

Note

New applications with iOS 8 or above as a deployment target may opt to link against AirshipKit.framework instead of libUAirship. Because AirshipKit is an embedded framework as opposed to a static library, applications using this framework can take advantage of features such as module-style import and automatic bridging to the Swift language. Be aware, however, that embedded frameworks are not supported on iOS 7 and below. Further instructions on how to set up AirshipKit can be found in the Including the Urban Airship SDK section below.

Build Settings and Phases

Modules are enabled by default in new projects starting with Xcode 5. We recommend enabling modules and the automatic linking of frameworks. In the project’s Build Settings, search for Enable Modules and set it to YES then set Link Frameworks Automatically to YES.

Including the Urban Airship SDK

  • Classic
  • CocoaPods
  • Carthage

Before you begin, make sure both the AirshipKit and Airship directories are in the top level of your app’s source directory.

  1. Include AirshipKit as a project dependency:

    Drag AirshipKit.xcodeproj out of the AirshipKit folder and into your app project in Xcode (directly under the top level of the project structure). Now AirshipKit will be built at compile-time for the active architecture.

  2. Link against the embedded framework:

    To link against the embedded framework, add the AirshipKit.framework file to the Embedded Binaries section in the General tab for your target. This should also add it to the Linked Frameworks and Libraries section.

  3. Check Linked Frameworks and Libraries:

    Make sure AirshipKit.framework shows up in the Linked Frameworks and Libraries section in the General tab for your target.

The library provides support for iOS 10 notification attachments, such as images, animated gifs, and video. In order to take advantage of this functionality, you will need to create a notification service extension alongside your main application.

Create a new iOS target in Xcode (File -> New -> Target) and select the Notification Service Extension type. Typically extensions are named with a suffix on the main application’s ID. In this example the bundle identifier would be com.urbanairship.sample.ExampleServiceExtension

Check your app’s target Embed App Extensions contains your newly created extension.

Drag the AirshipAppExtensions.framework into your app project.

Add AirshipAppExtensions to Target Dependencies in your extension’s Build Phases.

Link against AirshipAppExtensions.framework in your extension’s Build Phases.

Add a Copy Files phase for AirshipAppExtensions.framework and select “Frameworks” as the destination.

Modify your extension.

  1. Delete all dummy source code for your new extension.
  2. Import <AirshipAppExtensions/AirshipAppExtensions.h> if using Objective-C, or AirshipAppExtensions if using Swift, in NotificationService.
  3. Inherit from UAMediaAttachmentExtension in NotificationService.
// Notification.swift
import AirshipAppExtensions

class NotificationService: UAMediaAttachmentExtension {

}
// NotificationService.h
#import <AirshipAppExtensions/AirshipAppExtensions.h>

@interface NotificationService : UAMediaAttachmentExtension

@end

// NotificationService.m
#import <Foundation/Foundation.h>
#import "NotificationService.h"

@implementation NotificationService

@end

Before you begin, ensure that you have the CocoaPods dependency manager installed. You can do so by executing the following command:

$ gem install cocoapods

Specify the UrbanAirship-iOS-SDK in your podfile with the use_frameworks! option:

use_frameworks!

# Urban Airship SDK
target "<Your Target Name>" do
  pod 'UrbanAirship-iOS-SDK'
end

Install using the following command:

$ pod install

The library provides support for iOS 10 notification attachments, such as images, animated gifs, and video. In order to take advantage of this functionality, you will need to create a notification service extension alongside your main application and install the UrbanAirship-iOS-AppExtensions pod to this target.

Create a new iOS target in Xcode (File -> New -> Target) and select the Notification Service Extension type. Typically extensions are named with a suffix on the main application’s ID. In this example the bundle identifier would be com.urbanairship.sample.ExampleServiceExtension

Check your app’s target Embed App Extensions contains your newly created extension.

Specify the UrbanAirship-iOS-AppExtensions in your podfile and your newly created service extension as the target:

use_frameworks!

# Urban Airship SDK
target "<Your Service Extension Target Name>" do
  pod 'UrbanAirship-iOS-AppExtensions'
end

Install using the following command:

$ pod install

Modify your extension.

  1. Delete all dummy source code for your new extension.
  2. Import <AirshipAppExtensions/AirshipAppExtensions.h> if using Objective-C, or AirshipAppExtensions if using Swift, in NotificationService.
  3. Inherit from UAMediaAttachmentExtension in NotificationService.
// Notification.swift
import AirshipAppExtensions

class NotificationService: UAMediaAttachmentExtension {

}
// NotificationService.h
#import <AirshipAppExtensions/AirshipAppExtensions.h>

@interface NotificationService : UAMediaAttachmentExtension

@end

// NotificationService.m
#import <Foundation/Foundation.h>
#import "NotificationService.h"

@implementation NotificationService

@end

Before you begin, ensure you have carthage installed. Carthage can be installed using Homebrew. by executing the following commands:

$ brew update
$ brew install carthage

Specify the Urban Airship iOS SDK in your cartfile:

github "urbanairship/ios-library"

Execute the following command in the same directory as the cartfile:

carthage update

Once the update has completed, drag the built AirshipKit framework into your Xcode project from the Carthage build directory.

The library provides support for iOS 10 notification attachments, such as images, animated gifs, and video. In order to take advantage of this functionality, you will need to create a notification service extension alongside your main application.

Create a new iOS target in Xcode (File -> New -> Target) and select the Notification Service Extension type. Typically extensions are named with a suffix on the main application’s ID. In this example the bundle identifier would be com.urbanairship.sample.ExampleServiceExtension

Check your app’s target Embed App Extensions contains your newly created extension.

Drag the built AirshipAppExtensions framework into your Xcode project from the Carthage build directory.

Add AirshipAppExtensions framework to Target Dependencies in your extension’s Build Phases.

Link against AirshipAppExtensions.framework in your extension’s Build Phases.

Add a Copy Files phase for AirshipAppExtensions.framework and select “Frameworks” as the destination.

Modify your extension.

  1. Delete all dummy source code for your new extension.
  2. Import <AirshipAppExtensions/AirshipAppExtensions.h> if using Objective-C, or AirshipAppExtensions if using Swift, in NotificationService.
  3. Inherit from UAMediaAttachmentExtension in NotificationService.
// Notification.swift
import AirshipAppExtensions

class NotificationService: UAMediaAttachmentExtension {

}
// NotificationService.h
#import <AirshipAppExtensions/AirshipAppExtensions.h>

@interface NotificationService : UAMediaAttachmentExtension

@end

// NotificationService.m
#import <Foundation/Foundation.h>
#import "NotificationService.h"

@implementation NotificationService

@end

Enable Background Push

Enable background notifications that were introduced in iOS 7 by including the UIBackgroundModes key with the remote-notification value in your app’s Info.plist file. When viewing the plist in Xcode, UIBackgroundModes is displayed as Required background modes and remote-notification is displayed as App downloads content in response to push notifications.

You can also enable Background Modes and Remote notifications under the target’s Capabilities section:

Create AirshipConfig.plist

The Urban Airship SDK uses a .plist configuration file named AirshipConfig.plist to manage your production and development application profiles.

  1. Create two applications within your Urban Airship account: One for development and one for production. For example:

    1. Name_of_your_app_dev
    2. Name_of_your_app_prod
  2. Create an AirshipConfig.plist file.

  3. Set the following values to the ones in your applications:

    <?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>detectProvisioningMode</key>
      <true/>
      <key>developmentAppKey</key>
      <string>Your Development App Key</string>
      <key>developmentAppSecret</key>
      <string>Your Development App Secret</string>
      <key>productionAppKey</key>
      <string>Your Production App Key</string>
      <key>productionAppSecret</key>
      <string>Your Production App Secret</string>
    </dict>
    </plist>
    

Import the Required Header Files

At the top of your application delegate include the umbrella AirshipKit’s umbrella header:

import AirshipKit
#import <AirshipKit/AirshipKit.h>

If your applicaiton uses the static library, you must import the individual headers:

#import "UAirship.h"
#import "UAConfig.h"
#import "UAPush.h"

Starting Urban Airship Services

The Urban Airship SDK requires only a single entry point in the app delegate, known as takeOff. Inside your application delegate’s application:didFinishLaunchingWithOptions: method, initialize a shared UAirship instance by calling UAirship takeOff. This will bootstrap the SDK and look for settings specified in the AirshipConfig.plist file you created earlier. These can also be provided by passing an instance of UAConfig at runtime by calling UAirship takeOff:. UAirship takeOff must be called on the main thread, in this method, before it returns.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    // Populate AirshipConfig.plist with your app's info from https://go.urbanairship.com
    // or set runtime properties here.
    let config = UAConfig.default()

    // You can also programmatically override the plist values:
    // config.developmentAppKey = "YourKey"
    // etc.

    // Call takeOff (which creates the UAirship singleton)
    UAirship.takeOff(config)
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Populate AirshipConfig.plist with your app's info from https://go.urbanairship.com
    // or set runtime properties here.
    UAConfig *config = [UAConfig defaultConfig];

    // You can also programmatically override the plist values:
    // config.developmentAppKey = @"YourKey";
    // etc.

    // Call takeOff (which creates the UAirship singleton)
    [UAirship takeOff:config];
}

Retrieving your Channel ID

The Channel ID is a unique identifier that ties together an application/device pair on iOS devices. The Channel ID is used to target pushes to specific devices using the Urban Airship API.

The Channel ID will be logged for all apps. You can always get the channelID with a couple of extra lines to your application:

let channelID = UAirship.push().channelID
print("My Application Channel ID: \(channelID)")
NSString *channelID = [UAirship push].channelID;
NSLog(@"My Application Channel ID: %@", channelID);

Don’t worry if this value initially comes back as null on your app’s first run. The Channel ID will be created and persisted during registration.

Enabling User Notifications

The Urban Airship SDK makes a distinction between “user notifications”, which can be seen by the user, and other forms of push that allow you to send data to your app silently, or in the background. Enabling or disabling user notifications is a preference often best left up to the user, so by default, user notifications are disabled. For testing purposes, enabling notification at startup requires a few extra lines of code to your app delegate.

Set the user notification options on [UAirship push]. Sounds, alerts and badges are the default types. The library will register to receive these types of notifications as soon as user push notifications are enabled.

// Set the notification options required for the app (optional). This value defaults
// to badge, alert and sound, so it's only necessary to set it if you want
// to add or remove types.
UAirship.push().notificationOptions = [.alert, .badge, .sound]
// Set the notification options required for the app (optional). This value defaults
// to badge, alert and sound, so it's only necessary to set it if you want
// to add or remove types.
[UAirship push].notificationOptions = (UANotificationOptionAlert |
                                     UANotificationOptionBadge |
                                     UANotificationOptionSound);

To enable user notifications programmatically:

// User notifications will not be enabled until userPushNotificationsEnabled is
// set true on UAPush. Once enabled, the setting will be persisted and the user
// will be prompted to allow notifications. Normally, you should wait for a more appropriate
// time to enable push to increase the likelihood that the user will accept
// notifications.
UAirship.push().userPushNotificationsEnabled = true
// User notifications will not be enabled until userPushNotificationsEnabled is
// set YES on UAPush. Once enabled, the setting will be persisted and the user
// will be prompted to allow notifications. Normally, you should wait for a more appropriate
// time to enable push to increase the likelihood that the user will accept
// notifications.
[UAirship push].userPushNotificationsEnabled = YES;

Warning

A new default behavior was introduced in SDK 6.0.0 ensuring that once user push notifications have been enabled on iOS 8+, settings may only be modified from within the iOS Settings app. Applications should link directly to the application settings.

To disable user push settings on iOS 7 or below, set userPushNotificationsEnabled to NO. If a device is running iOS 8 or higher, we recommend that the application link directly to the application’s system push settings with the UIApplicationOpenSettingsURLString URL constant.

// Open the system settings app directly to this application (on iOS 8+)
if let url = URL(string: UIApplicationOpenSettingsURLString) {
     UIApplication.shared.openURL(url)
}
// Open the system settings app directly to this application (on iOS 8+)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];

Send a Test Notification

Now would be a great time to send a test notification. See Send Your First Notification to get started using our dashboard interface, or our API reference for more details.

App Transport Security

If your app is targeting iOS 9 or higher, Message Center content features such as Landing Pages will only be able to load SSL-encrypted web content out of the box. This change, known as App Transport Security, will not affect content hosted on Urban Airship’s servers, which is SSL-encrypted. While we generally recommend leaving these default settings alone, if your app will need to load plain HTTP content then you can whitelist individual domains in your app’s Info.plist file. For more information, see Apple’s App Transport Security Technote.

Reference, Migration Guides, and Troubleshooting

All the public classes and methods provided by the Urban Airship SDK have corresponding Appledoc entries in our Reference documentation.

We also publish an update to the SDK migration guide SDK migration guide every time the SDK is updated, to help make the update process as painless as possible.

Lastly, if you find yourself stuck, check our Troubleshooting guide before contacting support.

Configuration

The library provides a number of advanced configuration features that can be controlled with the UAConfig object. UAConfig objects can either be populated from your AirshipConfig.plist file, or at runtime using its exposed properties. See the UAConfig documentation for the full list of configuration options.

Auto-detect Production Mode

The library can automatically determine the production mode at runtime based on the current application provisioning profile. To enable this auto-detection, set detectProvisioningMode to YES.

Note

This functionality is supported only in versions 3.0.3+ of our iOS library.

Reset Message Center Users

Message Center user info is stored in the device keychain. This allows the user info to persist through app uninstalls and device upgrades, but this can cause problems during development if you want to test a fresh install. Expose a checkbox in the settings bundle set as a BOOL for the key “com.urbanairship.reset_keychain”. The next time the application starts, the user will automatically be reset once.

Automatic Integration

The Urban Airship library will automatically integrate with your app, so you do not need to implement any of the push-related UIApplicationDelegate protocol methods or pass notifications to the library. The library is able to do this by intercepting app delegate messages and forwarding them to your original app delegate, using a technique known as method swizzling.

This should be compatible with the vast majority of applications out of the box, but if you have advanced use cases for your app delegate, or if you would otherwise prefer to disable this behavior, you can set automaticSetupEnabled to NO in either your AirshipConfig.plist file or on a UAConfig object passed to [UAirship takeOff:].

If you choose to disable automatic integration, you will need to add the following code to your app delegate:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    // Set up the UNUserNotificationCenter delegate
    UNUserNotificationCenter.current().delegate = self
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    UAAppIntegration.application(UIApplication.shared, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}

func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
     UAAppIntegration.application(application, didRegister: notificationSettings)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    UAAppIntegration.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
    if let actionIdentifier = identifier {
        UAAppIntegration.application(application, handleActionWithIdentifier: actionIdentifier, forRemoteNotification: userInfo, completionHandler: completionHandler)
    }
}

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
    if let actionIdentifier = identifier {
        UAAppIntegration.application(application, handleActionWithIdentifier: actionIdentifier, forRemoteNotification: userInfo, withResponseInfo: responseInfo, completionHandler: completionHandler)
    }
}

// UNUserNotificationCenterDelegate methods

func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (options: UNNotificationPresentationOptions) -> Void) {
    UAAppIntegration.userNotificationCenter(center,
        willPresentNotification: notification,
        withCompletionHandler: completionHandler)
}

func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
    UAAppIntegration.userNotificationCenter(center,
    didReceiveNotificationResponse: response,
    withCompletionHandler: completionHandler)
}
// UIApplicationDelegate methods

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Set up the UNUserNotificationCenter delegate
    [UNUserNotificationCenter currentNotificationCenter].delegate = self;
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [UAAppIntegration application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    [UAAppIntegration application:application didRegisterUserNotificationSettings:notificationSettings];
}

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    [UAAppIntegration application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())handler {
    [UAAppIntegration application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:handler];
}

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())handler {
    [UAAppIntegration application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:handler];
}

// UNUserNotificationCenterDelegate methods

- (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    [UAAppIntegration userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
}

- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    [UAAppIntegration userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}

Logging

Log levels can be set independently for production and development modes. Set developmentLogLevel and productionLogLevel on your UAConfig.

Addressing Devices

To help target specific devices or users for a notification, we have Tags, Named Users and Tag Groups.

Tags

Tags are an easy way to group channels. Many tags can be applied to one or more devices. Then, a message sent to a tag will be sent out to all devices that are associated with that tag.

Tags are a good way to group device tokens by country, language, time zone or device type. These tags can be automatically generated using UATagUtils. It is good practice to minimize API calls when setting multiple tags. It’s more efficient to set them in groups.

Example:

Replace existing tags or set a new group of tags

UAirship.push().tags = ["one", "two", "three"]
UAirship.push().updateRegistration()
[UAirship push].tags = @[@"one", @"two", @"three"];
[[UAirship push] updateRegistration];

Add a single tag to the current device tag list and update the server immediately

UAirship.push().addTag("a_tag")
UAirship.push().updateRegistration()
[[UAirship push] addTag:@"a_tag"];
[[UAirship push] updateRegistration];

Remove a single tag from the current device tag list and update the server immediately

UAirship.push().removeTag("a_tag")
UAirship.push().updateRegistration()
[[UAirship push] removeTag:@"a_tag"];
[[UAirship push] updateRegistration];

Named Users

Named Users allow you to associate multiple devices to a single user or profile that may be associated with more than one device, e.g., an end-user’s iPhone and iPad. A device can have only one Named User, and a single Named User should not be associated with more than 20 devices. Named Users provide the ability to directly set tags on them (see Tag Groups).

By default, Named Users can only be associated server-side. A 403 Forbidden response is returned if Named User association is attempted when client-side association is disabled. In order to associate a Named User through the application, you must change the application’s Named User security setting to allow Named Users to be set from devices. Please visit the Settings documentation to enable this option.

Once the application has a channel ID, you can associate the channel to a Named User ID:

UAirship.namedUser().identifier = "NamedUserID"
[UAirship namedUser].identifier = @"NamedUserID";

To disassociate a channel from a Named User ID, set its ID to nil:

UAirship.namedUser().identifier = nil
[UAirship namedUser].identifier = nil;

Note

Associating the channel with a Named User ID, will implicitly disassociate the channel from the previously associated Named User ID, if it existed.

Tag Groups

Tag groups are configurable namespaces for organizing tags for the channel and Named User. Please view the Tag Groups documentation for more details. The Tag Group Walkthrough will guide you through the creation of your first Tag Group.

By default, Tag Groups can only be modified from the server. A 403 Forbidden response is returned if Tag Groups modification is attempted when client-side Tag Groups is disabled. In order to modify Tag Groups through the application, you must change the application’s Tag Group security settings to allow these tags to be set from devices. Please visit the Settings documentation to enable this option.

Channel Tag Groups

Once you have created a Tag Group, you can add or remove tags for the Tag Group on the channel. In this example, we have created a Tag Group loyalty.

Add tags to a Tag Group on the channel:

// Add tags to a Tag Group.
let tagsToAdd = ["silver-member", "gold-member"]
UAirship.push().addTags(tagsToAdd, group:"loyalty")

// Update the registration
UAirship.push().updateRegistration()
// Add tags to a Tag Group.
NSArray *tagsToAdd = @[@"silver-member", @"gold-member"];
[[UAirship push] addTags:tagsToAdd group:@"loyalty"];

// Update the registration.
[[UAirship push] updateRegistration];

Remove tags from a Tag Group on the channel:

// Remove tags from a Tag Group.
let tagsToRemove = ["bronze-member", "club-member"]
UAirship.push().removeTags(tagsToRemove, group:"loyalty")

// Update the registration
UAirship.push().updateRegistration()
// Remove tags from a Tag Group.
NSArray *tagsToRemove = @[@"bronze-member", @"club-member"];
[[UAirship push] removeTags:tagsToRemove group:@"loyalty"];

// Update the registration.
[[UAirship push] updateRegistration];

Note

If setting channel tags is enabled on the device, the predefined device Tag Group cannot be modified from the device and will log an error message. Disable channel tags by setting channelTagRegistrationEnabled to NO on UAPush if you want to modify this “default” Tag Group.

Named User Tag Groups

Named users provide the ability to directly set tags on them. Once you have created a Tag Group and associated a channel with the Named User, you can add or remove tags for the Tag Group on that Named User. In this example, we have created a Tag Group loyalty.

Add tags to a Tag Group on a Named User:

// Add tags to a Tag Group.
let tagsToAdd = ["silver-member", "gold-member"]
UAirship.namedUser().addTags(tagsToAdd, group:"loyalty")

// Apply the change.
UAirship.namedUser().updateTags()
// Add tags to a Tag Group.
NSArray *tagsToAdd = @[@"silver-member", @"gold-member"];
[[UAirship namedUser] addTags:tagsToAdd group:@"loyalty"];

// Apply the change.
[[UAirship namedUser] updateTags];

Remove tags from a Tag Group on a Named User:

// Remove tags from a Tag Group.
let tagsToRemove = ["silver-member", "gold-member"]
UAirship.namedUser().removeTags(tagsToRemove, group:"loyalty")

// Apply the change.
UAirship.namedUser().namedUser.updateTags()
// Remove tags from a Tag Group.
NSArray *tagsToRemove = @[@"silver-member", @"gold-member"];
[[UAirship namedUser] removeTags:tagsToRemove group:@"loyalty"];

// Apply the change.
[[UAirship namedUser] updateTags];

Custom Identifiers

In addition to Named Users, tags, and Tag Groups, you can assign up to 20 custom identifiers to users. Identifiers that define additional ways of looking at a user – for example, iOS Vendor ID, iOS Advertising ID, Google Analytics CID, or any other identifier you deem significant – can be added to your users.

Custom identifiers will be visible in the Connect data stream. We recommend adding any IDs that you may want to be visible in Connect, even if Connect does not yet support the relevant integration. Unlike the other identifiers (e.g., tags), you cannot use custom identifiers to target your users.

Note

Previous identifiers will be replaced by the new identifiers each time associateIdentifiers is called. It is a set operation.

Add a custom identifier:

// Get the current identifiers
let identifiers = UAirship.shared().analytics.currentAssociatedDeviceIdentifiers()

// Add a custom identifier
identifiers.setIdentifier("customIdentifier", forKey:"customkey")

// Associate the identifiers
UAirship.shared().analytics.associateDeviceIdentifiers(identifiers)
// Get the current identifiers
UAAssociatedIdentifiers *identifiers = [[UAirship shared].analytics currentAssociatedDeviceIdentifiers];

// Add a custom identifier
[identifiers setIdentifier:@"customIdentifier" forKey:@"customKey"];

// Associate the identifiers
[[UAirship shared].analytics associateDeviceIdentifiers:identifiers];

Add the iOS Advertising ID:

// Get the current identifiers
let identifiers = UAirship.shared().analytics.currentAssociatedDeviceIdentifiers()

// Set the advertising ID info
identifiers.advertisingID = ASIdentifierManager.sharedManager().advertisingIdentifier.UUIDString;
identifiers.advertisingTrackingEnabled = ASIdentifierManager.sharedManager().advertisingTrackingEnabled;
identifiers.vendorID = UIDevice.currentDevice().identifierForVendor?.UUIDString

// Associate the identifiers
UAirship.shared().analytics.associateDeviceIdentifiers(identifiers)
// Get the current identifiers
UAAssociatedIdentifiers *identifiers = [[UAirship shared].analytics currentAssociatedDeviceIdentifiers];

// Set the advertising ID info
identifiers.advertisingID = [[ASIdentifierManager sharedManager].advertisingIdentifier] UUIDString];
identifiers.advertisingTrackingEnabled = [ASIdentifierManager sharedManager].advertisingTrackingEnabled;
identifiers.vendorID = [[UIDevice currentDevice].identifierForVendor UUIDString];

// Associate the identifiers
[[UAirship shared].analytics associateDeviceIdentifiers:identifiers];

Push

Urban Airship’s push library provides a simple interface for managing push notifications within your iOS application.

Badges

You can set (or reset) the badge number within your application by setting applicationIconBadgeNumber:

UIApplication.shared.applicationIconBadgeNumber = 0;
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;

If you are using autobadge, in addition to setting the local badge number as shown above, you will need to make a device registration call to reset the server side counter:

UAirship.push().updateRegistration()
[[UAirship push] updateRegistration];

The application’s icon badge can be manipulated with convenience methods provided by UAPush:

// Set to bade to 1
UAirship.push().setBadgeNumber(1)

// Reset the badge
UAirship.push().resetBadge()
// Set to bade to 1
[[UAirship push] setBadgeNumber:1];

// Reset the badge
[[UAirship push] resetBadge];

A typical application will reset the badge any time a user starts the app or resumes it from the background. This can be done by making [UAPush resetBadge] calls from application:didFinishLaunchingWithOptions: and applicationDidBecomeActive. You may also wish to reset the badge after receiving a push when the application is running by calling reset from application:didReceiveRemoteNotification:fetchCompletionHandler.

If your application uses Urban Airship’s autobadge feature, enable client-side autobadge tracking in application:didFinishLaunchingWithOptions: before changing the badge value:

UAirship.push().isAutobadgeEnabled = true

// Reset the badge
UAirship.push().resetBadge()
[UAirship push].autobadgeEnabled = YES;

// Reset the badge
[[UAirship push] resetBadge];

This will ensure that the client will always sync badge changes with the server so that subsequent autobadge notifications will increment properly.

Quiet Time

// Set the quiet time from 7:30pm - 7:30am
UAirship.push().setQuietTimeStartHour(19, startMinute: 30, endHour: 7, endMinute: 30)

// Enable quiet time
UAirship.push().isQuietTimeEnabled = true

// Update registration
UAirship.push().updateRegistration()
// Set the quiet time from 7:30pm - 7:30am
[[UAirship push] setQuietTimeStartHour:19 startMinute:30 endHour:7 endMinute:30];

// Enable quiet time
[UAirship push].quietTimeEnabled = YES;

// Update registration
[[UAirship push] updateRegistration];

Handling Notifications

iOS handles push notifications received outside the application, but if a notification is received while the app is running, it is up to the application developer to handle it.

The sample UI includes an implementation of UAPushNotificationDelegate that handles alerts, sounds, and badges. However, if you wish to customize this behavior, you can provide your own implementation:

UAirship.push().pushNotificationDelegate = customPushDelegate
[UAirship push].pushNotificationDelegate = customPushDelegate;

Background Push

The library provides support for background notifications that were introduced in iOS 7. Background pushes are push notifications that can launch or resume the app in the background and fetch new content. It is highly recommended that developers enable this background mode in their applications.

To support this mode, first you must include the UIBackgroundModes key with the remote-notification value in your app’s Info.plist file. When viewing the plist in Xcode, UIBackgroundModes is displayed as Required background modes and remote-notification is displayed as App downloads content in response to push notifications.

You can also enable Background Modes and Remote notifications under the target’s Capabilities section:

Then, create a custom UAPushNotificationDelegate that implements receivedBackgroundNotification:completionHandler:, which will perform your task when a background push is received.

Example:

func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) {
   // Application received a background notification

   // Call the completion handler
   completionHandler(.noData)
}
-(void)receivedBackgroundNotification:(UANotificationContent *)notificationContent completionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // Application received a background notification

    // Call the completion handler
    completionHandler(UIBackgroundFetchResultNoData);
}

Note

If your application delegate implements application:didReceiveRemoteNotification:fetchCompletionHandler: on platforms that support background push, then your application delegate will no longer receive calls to application:didReceiveRemoteNotification:. It is still necessary to implement the non-completion handler version of the app delegate and UAPushNotificationDelegate if you are supporting older versions of iOS.

Finally, set the custom UAPushNotificationDelegate after takeOff.

UAirship.push().pushNotificationDelegate = customPushDelegate
[UAirship push].pushNotificationDelegate = customPushDelegate;

If background pushes are not working after following the above steps, see iOS Background Push Troubleshooting.

Foreground Presentation Options

The library provides support for foreground presentation options that were introduced in iOS 10. The three UNNotificationPresentationOptions constants are badge, sound, and alert. These constants specify how to present a notification in the foreground app.

An example of playing the sound associated with the notification and displaying the alert using the text provided by the notification:

func presentationOptions(for notification: UNNotification) -> UNNotificationPresentationOptions {
    return [.alert, .sound]
}
(UNNotificationPresentationOptions)presentationOptionsForNotification:(UNNotification *)notification {
    return UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound;
}

Interactive Notifications

Built-In Interactive Notification Types

Urban Airship provides a set of standard Interactive Notification types (See: Built-In Interactive Notification Types). It is the type that determines which buttons and corresponding labels will be available when you send a push. See the next section for where to specify that in the push payload. You control what happens when you send the push separately, by tying each button ID to a specific action.

Custom Interactive Notification Types (Categories)

In order to define a custom Interactive Notification type, you must register a new category.

Note

Urban Airship reserves category IDs prefixed with “ua_”. Any custom categories with that prefix will be dropped.

Custom UIUserNotificationCategories are supported by setting the set of categories on [UAirship push].userNotificationCategories.

Categories can be set after takeOff. After takeOff completes (i.e., application:didFinishLaunchingWithOptions: has returned), any modifications to the user notification categories will require an update to the registration settings by calling [UAirship push] updateRegistration].

Note

[UAirship push] updateRegistration] supplants [UAirship push] updateAPNSRegistration] and handles registration for both APNS and Channels.

Example:

// Define an action for the category
let categoryAction = UANotificationAction(identifier: "category_action",
                                               title: "Action!",
                                             options: [.authenticationRequired, .foreground, .destructive])

// Define the category
let category = UANotificationCategory(identifier: "custom_category",
                                         actions: [categoryAction],
                               intentIdentifiers: [],
                                         options: [])

// Set the custom categories
UAirship.push().customCategories = [category]

// Update registration
UAirship.push().updateRegistration()
// Define an action for the category
UANotificationAction *categoryAction = [UANotificationAction actionWithIdentifier: @"category_action"
                                                                            title:@"Action!"
                                                                          options:(UNNotificationActionOptionForeground |
                                                                                  UNNotificationActionOptionDestructive |
                                                                                  UNNotificationActionOptionAuthenticationRequired)];

// Define the category
UANotificationCategory *category = [UANotificationCategory categoryWithIdentifier:@"custom_category"
                                                                          actions:@[categoryAction]
                                                                intentIdentifiers:@[]
                                                                          options:UNNotificationCategoryOptionNone];

// Set the custom categories
[UAirship push].customCategories = [NSSet setWithArray:@[category]];

// Update registration
[[UAirship push] updateRegistration];

User Notification Action Callbacks

Custom-defined Interactive Notification types can either make use of Urban Airship’s pre-defined actions, or you can write new custom action code.

If you want to bypass Urban Airship’s actions, you can can receive callbacks through either the UNUserNotificationDelegate or UAPushNotificationDelegate.

UNUserNotificationDelegate:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
                                            withCompletionHandler completionHandler: @escaping () -> Void)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response
                                                                           withCompletionHandler:(void(^)())completionHandler

UAPushNotificationDelegate:

func receivedNotificationResponse(_ notificationResponse: UANotificationResponse,
                                       completionHandler: @escaping () -> Swift.Void);
-(void)receivedNotificationResponse:(UANotificationResponse *)notificationResponse
                  completionHandler:(void (^)())completionHandler;

Actions

Urban Airship Actions provides a convenient way to automatically perform tasks by name in response to push notifications, Message Center App Page interactions and JavaScript.

An action is an abstraction over a unary function, which takes an argument and performs a defined task, producing an optional result. Actions may restrict or vary the work they perform depending on the arguments they receive, which may include type introspection and runtime context.

The Urban Airship library comes with pre-made actions for common tasks such as setting/modifying tags, showing a landing page, enabling deep linking, displaying the inbox or inbox message, scheduling or canceling action schedules, and opening URLs out of the box. Actions can also be extended to enable custom application behaviors and engagement experiences.

In iOS, Actions are sent as part of the notification payload as top level key values, where the key is the name of the action and the value is the action’s argument. The argument can be any valid JSON value.

Actions are triggered with extra context in the form of a Situation. The different situations allows actions to determine if they should run or not, and possibly do different behavior depending on the situation.

Available Situations

Action Registry

The action registry is the central place to register actions by name. Each entry in the registry contains an action, the names that the action is registered under, a predicate that allows filtering when an action can run, and allows specifying alternative actions for different situations.

Registering an action:

UAirship.shared().actionRegistry.register(action, names: ["action_name", "action_alias"])
[[UAirship shared].actionRegistry registerAction:action names:@[@"action_name", @"action_alias"]];

Looking up an action entry:

let entry = UAirship.shared().actionRegistry.registryEntry(withName: "action_name")
UAActionRegistryEntry *entry = [[UAirship shared].actionRegistry registryEntryWithName:@"action_name"];

Setting a predicate:

// Predicate that only allows the action to run if it was launched from a push
func predicate(args: UAActionArguments) -> Bool {
    return args.situation == UASituation.launchedFromPush
}

// Update the predicate
UAirship.shared().actionRegistry.updatePredicate(predicate, forEntryWithName: "action_name")
// Predicate that only allows the action to run if it was launched from a push
UAActionPredicate predicate = ^(UAActionArguments *args) {
    return (BOOL)(args.situation == UASituationLaunchedFromPush);
};

// Update the predicate
[[UAirship shared].actionRegistry updatePredicate:predicate forEntryWithName:@"action_name"];

Triggering Actions

Actions can be triggered automatically through push via our REST API or the Automation composer. They can also be triggered automatically from a web view displaying message center content or an automation schedule. This interaction between web content and the Actions Framework is described in more detail in Custom HTML Templates. An in-depth description of action-based automation may be found below.

It’s possible to trigger actions manually as well, but it’s more convenient and safer to run actions through the UAActionRunner. The action runner is able to run an action by a name, or run an action directly.

// Run an action by name
UAActionRunner.runAction(withName: "action_name", value: "action_value", situation: UASituation.manualInvocation) { (result: UAActionResult) in
  print("Action finished!")
}

// Run an action directly
UAActionRunner.runAction(action, value: "action_value", situation: UASituation.manualInvocation) { (result: UAActionResult) in
  print("Action finished!")
}
// Optional completion handler
UAActionCompletionHandler completionHandler = ^(UAActionResult *result) {
    NSLog(@"Action finished!");
};

// Run an action by name
[UAActionRunner runActionWithName:@"action_name"
                            value:@"actionValue"
                        situation:UASituationManualInvocation
                completionHandler:completionHandler];

// Run an action directly
[UAActionRunner runAction:action
                    value:@"actionValue"
                situation:UASituationManualInvocation
        completionHandler:completionHandler];

Available Actions

Custom Actions

The action framework supports any custom actions. Create an action by extending the base action class or defining an action using blocks. After takeoff, register the action. The action can be triggered the same way as built-in actions.

Note

All actions are run on the main queue. If its possible for an action to block the main queue, it should perform its work on a background queue and call the UAActionCompletionHandler when finished.

Example:

let customAction = UAAction(block: { (args: UAActionArguments, handler: UAActionCompletionHandler) -> Void in
    print("Action is performing with UAActionArguments: \(args)")

    handler(UAActionResult.empty())
})
UAAction *customAction = [UAAction actionWithBlock: ^(UAActionArguments *args, UAActionCompletionHandler handler) {
    NSLog(@"Action is performing with UAActionArguments: %@", args);

    handler([UAActionResult emptyResult]);
}];

Register the action after takeoff:

UAirship.shared().actionRegistry.register(customAction, name:"custom_action")
[[UAirship shared].actionRegistry registerAction:customAction name:@"custom_action"];

On-Device Automation

On-device automation allows the Urban Airship SDK to execute actions when certain conditions are met upon the creation of an analytics event. These conditions are defined as triggers and range from app foreground to screen events. Actions can be scheduled either through an action or using the APIs exposed by the SDK.

Using the Automation Actions

The “schedule_actions” action enables scheduling through the Push API as an “app defined” action, through the Urban Airship JavaScript bridge, or directly in the SDK by manually running the action. There are several components to this schedule payload. A single schedule consists of the following fields:

  • "group" - Optional, group identifier. Useful for canceling and retrieving schedules for a specific campaign.
  • "start" - Optional, start time as an ISO 8601 timestamp. The time before the schedule starts listening for events.
  • "end" - Optional, end time as ISO 8601 timestamp. Once the schedule end time is reached, it will automatically be canceled.
  • "triggers" - Required, an array of triggers. Triggers cause the actions payload to execute.
  • "limit" - Optional, integer defaulting to 1. Number of times to trigger the actions payload before canceling the schedule.
  • "actions" - Required, map of actions payloads. Run when one or more of the triggers meets its goal.

Where a trigger consists of:

  • "type" - Required, the event type. Must be one of the valid types below. Each trigger type defines how the goal is incremented and the predicate argument.
  • "goal" - Required, the trigger’s goal. When the trigger’s goal is met, it will trigger the schedule’s actions to be executed.
  • "predicate" - Optional, predicate to filter out events.

A trigger may be one of the following types:

  • "custom_event_value" - Custom event’s value will be applied to the trigger’s goal. Predicate is applied to the event.
  • "custom_event_count" - Custom events will be counted towards the trigger’s goal. Predicate is applied to the event.
  • "foreground" - App foreground counts will be counted towards the trigger’s goal. Predicate is ignored.
  • "background" - App background counts will be counted towards the trigger’s goal. The predicate is ignored.
  • "region_enter" - Region enter events will be counted towards the trigger’s goal. Predicate is applied to the event.
  • "region_exit" - Region exit events will be counted towards the trigger’s goal. Predicate is applied to the event.
  • "screen" - Screen tracking. Predicate is applied against the screen’s name.

The predicate is the core logic within the trigger, presenting a set of structured conditions that are applied to an analytics event. It follows the same patterns as http://docs.urbanairship.com/api/ua.html#custom-event-selectors and may be a value matcher or logical expression. Syntax is as follows:

<predicate>        := <json_matcher> | <not> | <and> | <or>
    <not>              := { "not": { <predicate> } }
    <and>              := { "and": [<predicate>, <predicate>, …] }
    <or>               := { "or": [<predicate>, <predicate>, …] }
    <json_matcher>     := { <selector>, "value": { <value_matcher> }} | { "value": {<value_matcher>}}
       <selector>         := <scope>, "key": string | "key": string | <scope>
           <scope>            := "scope": string | "scope": [string, string, …]
    <value_matcher>    := <numeric_matcher> | <string_matcher> | <presence_matcher>
       <numeric_matcher>  := "equals": number | "at_least": number | "at_most": number | "at_least": number, "at_most": number
       <string_matcher>   := "equals": string
       <presence_matcher> := "is_present": boolean

Example payload:

The following payload is an example of how to set up an automation action using the scheduling action. For a given campaign, this schedule will display a landing page after a user has either visited a given page ten times or, as reported through a custom event, purchased items beyond a threshold of $30.

{
   "schedule_actions" :{
      "actions":{
         "landing_page_action":"https://www.urbanairship.com"
      },
      "limit": 1,
      "start": "2015-04-01T12:00:00",
      "end": "2017-05-01T12:00:00",
      "group": "campaign 1",
      "triggers": [
         {
            "type": "screen",
            "goal": 10,
            "predicate": {
               "value": {
                  "equals": "purchase_page"
               }
            }
         },
         {
            "type": "custom_event_count",
            "goal": 1.0,
            "predicate": {
               "and" : [
               {
                  "key": "event_name",
                  "value": {
                     "equals": "purchase"
                  }
               },
               {
                  "key": "price",
                  "scope": ["properties"],
                  "value": {
                     "at_least": "30.0"
                  }
               }]
            }
         }
      ]
   }
}

Just like scheduling actions, canceling scheduled actions can be done with the “cancel_scheduled_actions” action. Payloads may either contain one value:

  • "all" - Value for canceling all schedules

or the following two fields:

  • "groups" - Optional. Either a single group or an array of groups
  • "ids" - Optional. Either a single ID or an array of IDs

Example Payloads:

// Cancel all scheduled actions
"cancel_scheduled_actions": "all"

// Cancel specific groups and/or ids
"cancel_scheduled_actions": {
   "ids": ["id 1", "id 2"],
   "groups": ["campaign 1", "campaign 2"]
}

Using the Automation API

The API provided in UAAutomation allows for access to the automation storage. To schedule an action schedule, first create a UAActionScheduleInfo object.

let scheduleInfo = UAActionScheduleInfo { (builder) in
    let valueMatcher = UAJSONValueMatcher(whereStringEquals: "name")
    let jsonMatcher = UAJSONMatcher(valueMatcher: valueMatcher, key: UACustomEventNameKey)
    let predicate = UAJSONPredicate(jsonMatcher: jsonMatcher)

    let customEventTrigger = UAScheduleTrigger.customEventTrigger(with: predicate, count: 2)
    let foregroundEventTrigger = UAScheduleTrigger.foregroundTrigger(withCount: 2)

    builder.actions = ["add_tags_action": "my_tag"]
    builder.triggers = [customEventTrigger, foregroundEventTrigger]
    builder.group = "group_name";
    builder.limit = 4;
    builder.start = NSDate(timeIntervalSinceNow: 10) as Date;
    builder.end = NSDate(timeIntervalSinceNow: 1000) as Date;
}
UAActionScheduleInfo *scheduleInfo = [UAActionScheduleInfo actionScheduleInfoWithBuilderBlock:^(UAActionScheduleInfoBuilder *builder) {

    UAJSONValueMatcher *valueMatcher = [UAJSONValueMatcher matcherWhereStringEquals:@"name"];
    UAJSONMatcher *jsonMatcher = [UAJSONMatcher matcherWithValueMatcher:valueMatcher key:UACustomEventNameKey];
    UAJSONPredicate *predicate = [UAJSONPredicate predicateWithJSONMatcher:jsonMatcher];

    UAScheduleTrigger *customEventTrigger = [UAScheduleTrigger customEventTriggerWithPredicate:predicate count:2];
    UAScheduleTrigger *foregroundEventTrigger = [UAScheduleTrigger foregroundTriggerWithCount:2];

    builder.actions = @{@"add_tags_action": @"my_tag"};
    builder.triggers = @[customEventTrigger, foregroundEventTrigger];
    builder.group = @"group_name";
    builder.limit = 4;
    builder.start = [NSDate dateWithTimeIntervalSinceNow:10];
    builder.end = [NSDate dateWithTimeIntervalSinceNow:1000];
}];

When a UAActionScheduleInfo instance is scheduled, the module will return a UAActionSchedule instance with a unique ID generated for the the schedule.

UAirship.automation().scheduleActions(scheduleInfo) { (schedule) in
    print("Unique schedule identifier: \(schedule!.identifier)");
}
[[UAirship automation] scheduleActions:scheduleInfo completionHandler:^(UAActionSchedule *schedule) {
    NSLog(@"Unique schedule identifier: %@", schedule.identifier);
}];

The API provides methods for retrieving a single schedule, group of schedules, or all schedules.

UAirship.automation().getScheduleWithIdentifier(scheduleIdentifier) { (schedule) in
    // get schedule
}

UAirship.automation().getSchedules { (schedule) in
    // get all schedules
}

UAirship.automation().getSchedulesWithGroup("group_name") { (schedule) in
    // get all schedules of the given group.
}
[[UAirship automation] getScheduleWithIdentifier:scheduleIdentifier completionHandler:^(UAActionSchedule *schedule) {
    // get schedule
}];

[[UAirship automation] getSchedules:^(NSArray<UAActionSchedule *> *result) {
    // get all schedules
}];

[[UAirship automation] getSchedulesWithGroup:@"group_name" completionHandler:^(NSArray<UAActionSchedule *> *result) {
    // get all schedules of the given group.
}];

Lastly, a series of methods are provided for canceling a single schedule, a group or list of schedules, or all schedules.

// Cancels a schedule with the given identifier
UAirship.automation().cancelSchedule(withIdentifier: "some_scheduled_identifier")

// Cancels all schedules of the given group
UAirship.automation().cancelSchedules(withGroup: "some_group_name")

// Cancels all schedules
UAirship.automation().cancelAll()
// Cancels a schedule with the given identifier
[[UAirship automation] cancelScheduleWithIdentifier:@"some_scheduled_identifier"];

// Cancels all schedules of the given group
[[UAirship automation] cancelSchedulesWithGroup:@"some_group_name"];

// Cancels all schedules
[[UAirship automation] cancelAll];

Reports

Our iOS client library ships with analytics support out of the box, and by default, there are no explicit preferences you need to set in order to enable reporting. The integration is handled automatically unless you set automaticSetupEnabled to NO. If so, verify you followed the steps in Automatic Integration for proper integration.

Analytics Events and Uploading

Our client library stores events in a local database and uploads them periodically in a background thread. We’ve taken great care to make sure that the database won’t grow beyond a small fixed size, so extended periods of lost connectivity are nothing to worry about. The event upload thread is woken up when new events are triggered and goes to sleep when there are no more events to process, so the impact on battery life is negligible.

Custom Events

Custom events let you track user activities and key conversions in your application, and tie them back to corresponding push messaging campaigns. Custom events requires analytics to be enabled. If disabled, any event that is added to analytics will be ignored. For a more detailed explanation on custom events and possible use cases, see the Custom Events topic guide.

Example:

let event = UACustomEvent(name: "event_name", value: 123.12)

// Record the event
UAirship.shared().analytics.add(event)
UACustomEvent *event = [UACustomEvent eventWithName:@"event_name" value:@123.12];

// Record the event
[[UAirship shared].analytics addEvent:event];

Convenient templates are provided to create custom events for common account, media or retail related events. For more details see the iOS Event Templates topic guide.

Screen Tracking

The Urban Airship SDK gives you the ability to track what screen a user views within the application, as well as how long the user stayed on that screen and the user’s previous screen. These events then come through Urban Airship Connect and allow you to see the path that a user takes through an application, or to trigger actions based on a user visiting a particular area of the application.

Track a screen:

UAirship.shared().analytics.trackScreen("MainScreen")
// Record the event
[[UAirship shared].analytics trackScreen:@"MainScreen"];

When a new screen is tracked or when the application backgrounds, a screen tracking event will be generated. Normally, screen tracking should be called in a view controllers viewDidAppear: method, however it can be called anywhere in the application to start tracking a new screen event.

Disabling Analytics

If you do not wish to include analytics and reporting in your application, in your AirshipConfig.plist file, simply set the analyticsEnabled to NO.

You can disable analytics at runtime, which will delete any locally stored events and prevent any events from uploading.

UAirship.shared().analytics.enabled = false
[UAirship shared].analytics.enabled = NO;

Warning

Features that depend on analytics being enabled may not work properly if it’s disabled (reports, location segmentation, region triggers, push to local time).

Message Center

Displaying the Message Center

The default message center can be displayed at any time by calling display on UADefaultMessageCenter instance.

UAirship.defaultMessageCenter().display()
[[UAirship defaultMessageCenter] display];

Styling the Message Center

The default message center’s look can be customized by creating a UADefaultMessageCenterStyle instance, setting its style properties, and then setting the style property of the Message Center instance to the customized style instance:

let style = UADefaultMessageCenterStyle()

// 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.cellTitleFont = UIFont(name: "Roboto-Bold", size: 14.0)
style.cellDateFont = UIFont(name: "Roboto-Light", size: 12.0)

// Set the style
UAirship.defaultMessageCenter().style = style
UADefaultMessageCenterStyle *style = [UADefaultMessageCenterStyle style];

// 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.cellTitleFont = [UIFont fontWithName:@"Roboto-Bold" size:13.0];
style.cellDateFont = [UIFont fontWithName:@"Roboto-Light" size:12.0];

// Set the style
[UAirship defaultMessageCenter].style = style;

This can also be done without writing code by creating a plist file:

<?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>#eaaa00</string>
        <key>iconsEnabled</key>
        <false/>
        <key>cellTitleFont</key>
        <dict>
                <key>fontName</key>
                <string>Roboto-Bold</string>
                <key>fontSize</key>
                <string>13</string>
        </dict>
        <key>cellDateFont</key>
        <dict>
                <key>fontName</key>
                <string>Roboto-Light</string>
                <key>fontSize</key>
                <string>17</string>
        </dict>
</dict>
</plist>

To initialize a message center style from the plist, use the [UADefaultMessageCenter styleWithContentsOfFile] method:

let style = UADefaultMessageCenterStyle(contentsOfFile:plistFileName)
UADefaultMessageCenterStyle *style = [UAMessageCenterStyle styleWithContentsOfFile:plistFileName];

Aside from setting the style directly, as shown above, you can also apply this style to the default message center directly in your AirshipConfig.plist by setting the messageCenterStyleConfig key with the name of your plist file:

<key>messageCenterStyleConfig</key>
<string>Your Message Center Style Config File</string>

Message Center Filtering

Sometimes it can be useful to filter the contents of the message center according to some predetermined pattern. To facilitate this, use the [UADefaultMessageCenter filter] property to set a predicate. Once set, only messages that match the predicate will be displayed. As a simple example, the filter shown below keeps only messages whose titles contain the string “Cool”.

UAirship.defaultMessageCenter().filter = NSPredicate { (evaluatedObject, _) in
     let message = evaluatedObject as! UAInboxMessage
     let titleString:NSString = message.title as NSString
     return titleString.contains("Cool")
 };
[UAirship defaultMessageCenter].filter = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    UAInboxMessage *message = (UAInboxMessage *)evaluatedObject;
    return [message.title containsString:@"Cool"];
}];

Overriding the default Message Center

For custom Message Center implementations, initialize the UAInboxDelegate with the custom Message Center implementation’s view controller. This is necessary to ensure that the default modal Message Center is not displayed on top of the custom Message Center when a message is received.

self.inboxDelegate = CustomInboxDelegate()
UAirship.inbox().delegate = self.inboxDelegate
self.inboxDelegate = [[CustomInboxDelegate alloc] init];
[UAirship inbox].delegate = self.inboxDelegate;

Badge Updates

The value of the badge can be set in a number of ways. Most often, the number corresponds with some notion of unread or unviewed content, e.g., email messages or friend requests. The most common way to update the badge is directly through APNS, either by passing along the badge integer in the payload of a push notification, or by telling the app to adjust the value using our autobadge feature.

For applications implementing the Message Center, you may want the badge value to represent the actual state of unread messages in the Message Center. If so, then you need to set this behavior in the application delegate. Here is an example of how to set the current badge to the current unread message count when the application moves to inactive state:

func applicationWillResignActive(application: UIApplication) {
    if (UAirship.inbox().messageList.unreadCount >= 0) {
        application.applicationIconBadgeNumber = UAirship.inbox().messageList.unreadCount
    }
}
- (void)applicationWillResignActive:(UIApplication *)application {
    if ([UAirship inbox].messageList.unreadCount >= 0) {
        application.applicationIconBadgeNumber = [UAirship inbox].messageList.unreadCount;
    }
}

If you use this method then you should never include badge values in the payload of push notifications, because Message Center will override them.

In-App Messaging

In-app messages are banner notifications that appear inside of your app. Aside from giving you the power to engage with users as they browse your app, they also allow you to reach opted out users in iOS 8 and above, a segment of the app audience that has previously been difficult to target.

Requirements

In-app messaging requires iOS 7+ and for Background Push to be enabled in your app’s capabilities.

A Note on Background Notifications

Without the remote notifications background mode, apps are limited to receiving silent pushes in the foreground only. On iOS 8, background notifications can be received even if the user has opted out or was never prompted. For the widest possible reach, you should encourage your users to upgrade to iOS 8.

Display

By default, in-app messages will only display a single banner on app foreground after a three-second delay. You can change this by setting the displayDelay property on [UAirship inAppMessaging]:

Adjusting the auto-display delay to five seconds:

UAirship.inAppMessaging().displayDelay = 5
[UAirship inAppMessaging].displayDelay = 5;

Display ASAP Mode

Enabling display ASAP mode will cause the SDK to display the in-app message on arrival. If the app is currently in the background, the in-app message will be displayed on the next foreground after the auto display delay. If a message is already displaying when a new in-app message is received, the new message will be displayed once the currently displayed message is dismissed, after the autodisplay delay.

Enabling display ASAP:

UAirship.inAppMessaging().displayASAPEnabled = true
[UAirship inAppMessaging].displayASAPEnabled = YES;

Note

Exercise caution when using display ASAP mode. The default auto display behavior for in-app messages is designed to offer a balance between immediacy and user experience, by minimizing the chance of accidentally disrupting the user’s activities in your app. Push automation may also occasionally see latencies that result in associated in-app messages being displayed in unexpected contexts.

Manual Display

The automatic display of in-app messages can be disabled, either to block message display in response to user preferences or to manually control when and where messages should be displayed:

UAirship.inAppMessaging().isAutoDisplayEnabled = false
[UAirship inAppMessaging].isAutoDisplayEnabled = NO;

In the latter case, message display can be triggered manually so that the most recent pending message is displayed at a time more appropriate for the app:

UAirship.inAppMessaging().displayPendingMessage()
[[UAirship inAppMessaging] displayPendingMessage];

Styling and Custom UI

Sending in-app message color overrides is supported through the composer and push API. Additional advanced styling can also be achieved by setting optional properties on the UAInAppMessaging class:

public var font: UIFont
public var defaultPrimaryColor: UIColor
public var defaultSecondaryColor: UIColor
@property(nonatomic, strong) UIFont *font;
@property(nonatomic, strong) UIColor *defaultPrimaryColor;
@property(nonatomic, strong) UIColor *defaultSecondaryColor;

More comprehensive custom UI is possible by setting an optional UAInAppMessageControllerDelegate on the UAInAppMessaging object, which is used to configure details such as custom message views and animation hooks:

UAirship.inAppMessaging().messageControllerDelegate = myMessageControllerDelegate
[UAirship inAppMessaging].messageControllerDelegate = myMessageControllerDelegate;

Required methods for the UAInAppMessageControllerDelegate protocol provide the SDK custom view and control classes at display time, configured using the data contained in the associated UAInAppMessageControllerDelegate object:

public func viewForMessage(message: UAInAppMessage, parentView: UIView) -> UIView
public func messageView(messageView: UIView, buttonAtIndex index: UInt) -> UIControl
- (UIView *)viewForMessage:(UAInAppMessage *)message parentView:(UIView *)parentView;
- (UIControl *)messageView:(UIView *)messageView buttonAtIndex:(NSUInteger)index;

Optional methods allow for additional customization handling touch state (such as inverting colors when the message view is tapped) and the animation of messaging display and dismissal:

optional public func messageView(messageView: UIView, didChangeTouchState touchDown: Bool)
optional public func messageView(messageView: UIView, animateInWithParentView parentView: UIView, completionHandler: () -> Void)
optional public func messageView(messageView: UIView, animateOutWithParentView parentView: UIView, completionHandler: () -> Void)
- (void)messageView:(UIView *)messageView didChangeTouchState:(BOOL)touchDown;
- (void)messageView:(UIView *)messageView animateInWithParentView:(UIView *)parentView completionHandler:(void (^)(void))completionHandler;
- (void)messageView:(UIView *)messageView animateOutWithParentView:(UIView *)parentView completionHandler:(void (^)(void))completionHandler;

Interactive Notification Compatibility Classes

In order to support interactive notification buttons on in-app messages for iOS 7, we’ve added a set of compatibility classes that mirror the functionality of apple’s user notification category and action model objects:

Use these classes instead of the corresponding UIUserNotificationCategory and UIUserNotificationAction classes if you wish to add custom interactive push buttons to in-app messages in iOS 7.

Location

Urban Airship UALocation exposes a very simple, high-level API for requesting location.

Enabling location updates can be done with the following:

UAirship.location().isLocationUpdatesEnabled = true
[UAirship location].locationUpdatesEnabled = YES;

When location updates are enabled for the first time it will prompt the user for permission. This behavior can be changed by disabling autoRequestAuthorizationEnabled:

UAirship.location().isAutoRequestAuthorizationEnabled = false
[UAirship location].autoRequestAuthorizationEnabled = NO;

Once the app backgrounds, location updates will be suspended by default, then resumed again once the app is foregrounded. To allow location updates to continue in the background, enable backgroundLocationUpdatesAllowed:

UAirship.location().isBackgroundLocationUpdatesAllowed = true
[UAirship location].backgroundLocationUpdatesAllowed = YES;

SDK Localization

The Urban Airship SDK provides localization for interactive notification buttons and the Message Center for the following languages: English, Chinese (Traditional), Chinese (Simplified), German, Hungarian, Slovak, Russian, Portuguese (Portugal), Portuguese (Brazil), French, Danish, Turkish, Norwegian, Romanian, Polish, Finnish, Dutch, Hindi, Spanish (Latin American), Spanish (Spain), Hebrew, Czech, Italian, Vietnamese, Thai, Japanese, Arabic, Korean, Malay, Indonesian, and Swedish.

To add translations for another language, follow these steps:

  1. Copy the UrbanAirship.strings file and translate it into another language:

    "ua_notification_button_yes" = "Yes";
    "ua_notification_button_no" = "No";
    "ua_notification_button_shop_now" = "Shop Now";
    "ua_notification_button_buy_now" = "Buy Now";
    "ua_notification_button_follow" = "Follow";
    "ua_notification_button_unfollow" = "Unfollow";
    "ua_notification_button_opt_in" = "Opt-in";
    "ua_notification_button_opt_out" = "Opt-out";
    "ua_notification_button_remind" = "Remind Me Later";
    "ua_notification_button_share" = "Share";
    "ua_notification_button_accept" = "Accept";
    "ua_notification_button_decline" = "Decline";
    "ua_notification_button_download" = "Download";
    "ua_notification_button_more_like" = "More Like This";
    "ua_notification_button_less_like" = "Less Like This";
    "ua_notification_button_like" = "Like";
    "ua_notification_button_dislike" = "Dislike";
    "ua_notification_button_copy" = "Copy";
    "ua_notification_button_save" = "Save";
    "ua_notification_button_add_to_calendar" = "Add to Calendar";
    "ua_notification_button_add" = "Add";
    "ua_notification_button_tell_me_more" = "Tell Me More";
    "ua_notification_button_send_info" = "Send Info";
    "ua_notification_button_rate_now" = "Rate Now";
    "ua_notification_button_search" = "Search";
    "ua_notification_button_book_now" = "Book Now";
    "ua_share_dialog_title" = "Share";
    "ua_channel_notification_ticker" = "Urban Airship Channel";
    "ua_channel_copy_toast" = "Channel copied to clipboard!";
    "ua_message_center_title" = "Message Center";
    "ua_empty_message_list" = "No messages";
    "ua_message_not_selected" = "No messages selected";
    "ua_mc_failed_to_load" = "Unable to load message. Please try again later.";
    "ua_retry_button" = "Retry";
    "ua_ok" = "OK";
    "ua_cancel" = "Cancel";
    "ua_select_none" = "Select None";
    "ua_select_all" = "Select All";
    "ua_connection_error" = "Connection Error";
    "ua_mark_read" = "Mark Read";
    "ua_delete" = "Delete";
    
  2. Add the new localization to the application in the project’s info section.