The Iterable iOS SDK is a Swift implementation of an iOS client for Iterable, for iOS versions 9.0 and higher.
Before starting with the SDK, you will need to set up Iterable push notifications for your app.
For more information, read Iterable's Setting up iOS Push Notifications guide.
Iterable's iOS SDK can be installed using Carthage or CocoaPods.
To use Carthage to install Iterable's iOS SDK, first install Carthage. Then, follow these steps:
-
If it does not yet exist, create a file named Cartfile in the same directory as your Xcode project.
-
Edit Cartfile, adding the following line:
github "Iterable/swift-sdk" ~> 6.1.0
-
In the terminal, in the same directory as your Cartfile, run the following command:
carthage update
-
In Xcode, navigate to the Build Phases section for your app's target. Click the + icon and select New Run Script Phase. A Run Script section will appear.
-
In the Run Script section, below the Shell input, add the following command:
/usr/local/bin/carthage copy-frameworks
-
In the Input Files section, click + and add the following path:
$(SRCROOT)/Carthage/Build/iOS/IterableSDK.framework
-
In the Output Files section, add the path to the copied framework:
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/IterableSDK.framework
-
Add <Xcode project directory>/Carthage/Build/iOS/IterableSDK.framework to your Xcode project by dragging it into the Xcode Project Navigator. When prompted by Xcode, add the framework to your app's target.
-
If your app will be using push notifications that contain media attachments (images, etc.), repeat steps 6 through 8, substituting IterableAppExtensions.framework for IterableSDK.framework. In step 8, add IterableAppExtensions.framework to your project's Notification Service Extension target (instead of the app target).
For more information, take a look at the Carthage documentation.
To use CocoaPods to install Iterable's iOS SDK, first install CocoaPods. Then, follow these steps:
-
If your project does not yet have a Podfile, create one.
-
In the terminal, navigate to the directory containing your project's .xcodeproj file
-
Run the following command:
pod init
-
-
Edit your project's Podfile.
-
Add the Iterable-iOS-SDK pod to your projec's app target.
-
If your app will receive push notifications containing media attachments (images, etc.), add the Iterable-iOS-AppExtensions pod to your project's Notification Service Extension target.
After these changes, your Podfile should look similar to the following:
platform :ios, '11.0' use_frameworks! target 'swift-sample-app' do pod 'Iterable-iOS-SDK' end target 'swift-sample-app-notification-extension' do pod 'Iterable-iOS-AppExtensions' end
You must include
use_frameworks!
in your Podfile, no matter if your app is based on Swift or Objective-C. If your project cannot use this option, install the SDK manually. -
-
In the terminal, run the following command to install the SDK (and app extensions, if necessary):
pod install
This will create an .xcworkspace file. To open your project in Xcode, use this file instead of the .xcodeproj file.
For more information, take a look at the CocoaPods documentation.
Attached to the release, you will find two framework bundles: IterableSDK.framework and IterableAppExtensions.framework.
-
In Xcode, choose the target for your app. Now, add the IterableSDK.framework to the Embedded Binaries section. If you want to use an Iterable Rich Notification Extension, you will have to add IterableAppExtensions.framework to the embedded binaries section as well.
-
If you want to use an Iterable Rich Notification Extension, you will need to add IterableAppExtension.framework to Linked Frameworks and Libraries section of your app extension target (not app target). Please note that you will have to add the IterableAppExtension.framework bundle to both the app target (step 1) and app extension target (step 2) of your project. In the app target, it goes in the Embedded Binaries section and in app extension target it goes in the Linked Frameworks and Libraries section.
-
In build settings, set Always Embed Swift Standard Libraries setting to Yes. This is necessary for both Swift and Objective-C projects.
-
Version 6.1.0 of the SDK requires Xcode 10.2.
-
In-app messages:
spawnInAppNotification
-
spawnInAppNotification
is no longer needed and will fail to compile. The SDK now displays in-app messages automatically. For more information, see In-app messages. -
There is no need to poll the server for new messages.
-
-
In-app messages: handling manually
-
To control when in-app messages display (rather than displaying them automatically), set
IterableConfig.inAppDelegate
(anIterableInAppDelegate
object). From itsonNew
method, return.skip
. -
To get the queue of available in-app messages, call
IterableApi.inAppManager.getMessages()
. Then, callIterableApi.inAppManager.show(message)
to show a specific message. -
For more details, see In-app messages.
-
-
In-app messages: custom actions
-
This version of the SDK reserves the
iterable://
URL scheme for Iterable-defined actions handled by the SDK and theaction://
URL scheme for custom actions handled by the mobile application's custom action handler. For more details, see Handling in-app message buttons and links. -
If you are currently using the
itbl://
URL scheme for custom actions, the SDK will still pass these actions to the custom action handler. However, support for this URL scheme will eventually be removed (timeline TBD), so it is best to move to theaction://
URL scheme as it's possible to do so.
-
-
Consolidated deep link URL handling
- By default, the SDK handles deep links with the the URL delegate
assigned to
IterableConfig
. Follow the instructions in Deep Links to migrate any existing URL handling code to this new API.
- By default, the SDK handles deep links with the the URL delegate
assigned to
For sample projects, look at the following repositories:
Follow these instructions to configure the Iterable iOS SDK:
To use the IterableSDK module, import it at the top of your Objective-C or Swift files:
Swift
// In AppDelegate.swift file
// and any other file where you are using IterableSDK
import IterableSDK
Objective-C
// In AppDelegate.m file
// and any other file where you are using IterableSDK
@import IterableSDK;
In the application:didFinishLaunchingWithOptions:
method of your app
delegate, call initialize(apiKey:launchOptions:config:)
, passing in your
Iterable API key:
Swift
let config = IterableConfig()
config.pushIntegrationName = "<your-iterable-push-integration-name>"
IterableAPI.initialize(apiKey: "<your-api-key>", launchOptions: launchOptions, config:config)
Objective-C
IterableConfig *config = [[IterableConfig alloc] init];
config.pushIntegrationName = @"<your-iterable-push-integration-name>";
[IterableAPI initializeWithApiKey:@"<your-api-key>" launchOptions:launchOptions config:config]
Once you have an email address or user ID for your app's current user, set
IterableAPI.email
or IterableAPI.userId
. For example:
โ Don't specify both email and userId in the same session, as they will be treated as different users by the SDK. Only use one type of identifier, email or userId, to identify the user. Swift
IterableAPI.email = "[email protected]"
Objective-C
IterableAPI.email = @"[email protected]";
Your app will not be able to receive push notifications until you set one of thes values.
For Iterable to send push notifications to an iOS device, it must know the unique token assigned to that device by Apple.
Iterable uses silent push notifications to tell iOS apps when to fetch new in-app messages from the server. Because of this, your app must register for remote notifications with Apple even if you do not plan to send it any push notifications.
IterableConfig.autoPushRegistration
determines whether or not the SDK will:
- Automatically register for a device token when the the SDK is given a new email address or user ID.
- Disable the device token for the previous user when a new user logs in.
If IterableConfig.autoPushRegistration
is true
(the default value):
- Setting
IterableAPI.email
orIterableAPI.userId
causes the SDK to automatically call theregisterForRemoteNotifications()
method onUIApplication
and pass the resulting device token to theapplication(_:didRegisterForRemoteNotificationsWithDeviceToken:)
method on the app delegate.
If IterableConfig.autoPushRegistration
is false
:
- After setting
IterableAPI.email
orIterableAPI.userId
, you must manually call theregisterForRemoteNotifications()
method onUIApplication
. This will fetch the device token from Apple and pass it to theapplication(_:didRegisterForRemoteNotificationsWithDeviceToken:)
method on the app delegate.
To send the device token to Iterable and save it on the current user's
profile, call IterableAPI.register(token:)
from the
application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
method on UIApplicationDelegate
. For example:
Swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
IterableAPI.register(token: deviceToken)
}
Objective-C
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[IterableAPI registerToken:deviceToken];
}
If you are planning to send push notifications to your app, complete the steps in this section.
iOS apps must receive user permission to display push notification alerts,
play push notification sounds, or update icon badges based on push
notifications. If you are planning to send these types of push notifications,
call Apple's requestAuthorization
method on UNNotificationCenter
.
to prompt the user for these permissions.
Swift
UNUserNotificationCenter.current().requestAuthorization(options:[.alert, .badge, .sound]) { (success, error) in
// ...
}
Objective-C
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions: (UNAuthorizationOptionAlert + UNAuthorizationOptionBadge + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// ...
}];
For more information, take a look at the following documents from Apple:
For push notifications to contain images, animated GIFs, video, or action buttons, you must create a Notification Service Extension.
The Iterable iOS SDK provides a Notification Service Extension implementation that handles media attachments and action buttons. To use it:
- Include Iterable-iOS-AppExtensions in your Podfile, as explained above.
- Create a new target of type Notification Service Extension in your Xcode project.
- If you are calling Iterable SDK from Swift, edit the
NotificationService
class (auto-generated by Xcode) so that it extendsITBNotificationServiceExtension
. - If you are using Objective-C, use delegation instead of inheritance.
For example:
Swift
With Swift, use inheritance:
import UserNotifications
import IterableAppExtensions
class NotificationService: ITBNotificationServiceExtension {
}
Objective-C
With Objective-C, use delegation:
// File: NotificationService.m
#import "NotificationService.h"
@import IterableAppExtensions;
@interface NotificationService ()
@property (nonatomic, strong) ITBNotificationServiceExtension *baseExtension;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.baseExtension = [[ITBNotificationServiceExtension alloc] init];
[self.baseExtension didReceiveNotificationRequest:request withContentHandler:contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
[self.baseExtension serviceExtensionTimeWillExpire];
}
@end
When a new user logs in to your app on the same device that a previous user had been using, you'll typically want to disable push notifications to the previous user (for that app/device combination only).
If IterableConfig.autoPushRegistration
is true
(the default value), the
SDK automatically disables push notifications to the previous user when you
provide a new value for IterableAPI.email
or IterableAPI.userId
.
If IterableConfig.autoPushRegistration
is false
, or if you need to
disable push notifications for a user before a new user logs in to your app,
manually call IterableAPI.disableDeviceForCurrentUser()
. This method only
works if you have previously called IterableAPI.register(token:)
.
If the previous user logs back in later, call IterableAPI.register(token:)
to again register that user for push notifications on that app/device
combination.
When the user taps on a push notification or one of its action buttons, the
system calls the UNUserNotificationCenterDelegate
object's
userNotificationCenter(_:didReceive:withCompletionHandler:)
method.
From this method, call the userNotificationCenter(_:didReceive:withCompletionHandler:)
method on IterableAppIntegration
. This tracks a push open event and
performs the associated action.
Swift
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
IterableAppIntegration.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}
Objective-C
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[IterableAppIntegration userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
For more information, see the app delegate in the example app.
A deep link is a URI that links to a specific location within your mobile app. The following sections describe how to work with deep links using Iterable's iOS SDK.
Push notifications and action buttons may have openUrl
actions attached to them. When a URL is specified, the SDK first calls the urlDelegate
object specified on your IterableConfig
object. You can use this delegate to handle openUrl
actions the same way as you handle normal deep links. If the delegate is not set or if it returns false
(the default), the SDK will open the URL with Safari. If, upon receiving a deep link, you want to navigate to a specific view controller in your app, do so in the urlDelegate
.
In the code below, DeepLinkHandler
is a custom handler which is reponsible for deep link navigation. You have to provide an implementation for deep link navigation. Please see the sample application for a reference implementation for DeeplinkHandler
.
Swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
// Initialize Iterable API
let config = IterableConfig()
...
config.urlDelegate = self
IterableAPI.initialize(apiKey: apiKey, launchOptions:launchOptions, config: config)
...
}
// Iterable URL Delegate. It will be called when you receive
// an `openUrl` event from push notification.
func handle(iterableURL url: URL, inContext context: IterableActionContext) -> Bool {
return DeeplinkHandler.handle(url: url)
}
Objective-C
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// Initialize Iterable SDK
IterableConfig *config = [[IterableConfig alloc] init];
...
config.urlDelegate = self;
[IterableAPI initializeWithApiKey:@"YOUR API KEY" launchOptions:launchOptions config:config];
...
}
- (BOOL)handleIterableURL:(NSURL *)url context:(IterableActionContext *)context {
// Assuming you have a DeeplinkHandler class that handles all deep link URLs and navigates to the right place in the app
return [DeeplinkHandler handleUrl:url];
}
For Universal Links to work with email link rewriting, set up an apple-app-site-association file in your Iterable project.
When a user clicks a link in an email, the SDK will call the
application(_:continue:restorationHandler:)
method of your UIApplicationDelegate
. If you already have an Iterable
urlDelegate
defined (see Handling Links from Push Notifications,
the same handler can be used for email deep links by calling
handle(universalLink:)
).
Swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
guard let url = userActivity.webpageURL else {
return false
}
// This will track the click, retrieve the original URL and call `handleIterableURL:context:` with the original URL
return IterableAPI.handle(universalLink: url)
}
Objective-C
- (BOOL)application:(UIApplication *)application continueUserActivity(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
// This will track the click, retrieve the original URL and call `handleIterableURL:context:` with the original URL
return [IterableAPI handleUniversalLink:userActivity.webpageURL];
}
Deferred deep linking allows a user who does not have a specific app installed to:
- Click on a deep link that would normally open content in that app.
- Install the app from the App Store.
- Open the app and immediately see the content referenced by the link.
As the name implies, the deep link is deferred until the app has been installed.
After tapping a deep link in an email from an Iterable campaign, users without the associated app will be directed to the App Store to install it. If the app uses the Iterable iOS SDK and has deferred deep linking enabled, the content associated with the deep link will load on first launch.
Set IterableConfig.checkForDeferredDeeplink
to true
to enable deferred
deep linking with the Iterable iOS SDK.
In-app messages are handled via silent push messages from the server. When your application receives a silent push, call the Iterable iOS SDK in your app delegate, as follows:
Swift
// In AppDelegate.swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
IterableAppIntegration.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[IterableAppIntegration application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
By default, when an in-app message arrives from the server, the SDK automatically shows it if the app is in the foreground. If an in-app message is already showing when the new message arrives, the new in-app message will be shown 30 seconds after the currently displayed in-app message closes (see how to change this default value). Once an in-app message is shown, it will be "consumed" from the server queue and removed from the local queue as well. There is no need to write any code to get this default behavior.
An incoming in-app message triggers a call to the onNew
method of IterableConfig.inAppDelegate
(an object of type IterableInAppDelegate
). To override the default behavior, set IterableConfig.inAppDelegate
to a custom class that overrides the onNew
method. onNew
should return .show
to show the incoming in-app message or .skip
to skip showing it.
Swift
class YourCustomInAppDelegate : IterableInAppDelegate {
func onNew(message: IterableInAppMessage) -> InAppShowResponse {
// perform custom processing
// ...
return .show // or .skip
}
}
// ...
let config = IterableConfig()
config.inAppDelegate = YourCustomInAppDelegate()
IterableAPI.initialize(apiKey: "YOUR API KEY", launchOptions: nil, config: config)
Objective-C
// Implement this method in your custom class that implements IterableInAppDelegate
// This will most likely be the global AppDelegate class.
- (enum InAppShowResponse)onNewMessage:(IterableInAppMessage * _Nonnull)message {
// perform custom processing
// ...
return InAppShowResponseShow; // or InAppShowResponseSkip
}
// ...
// Now set this custom class in IterableConfig
IterableConfig *config = [[IterableConfig alloc] init];
config.inAppDelegate = self; // or other class implementing the protocol
[IterableAPI initializeWithApiKey:@"YOUR API KEY" launchOptions:launchOptions config:config];
Until they are consumed by the app, all in-app messages that arrive from the server are stored in a local queue. To access this local queue, use the read-only IterableAPI.inAppManager
property (an object which conforms to the InAppManager
protocol). By default, all in-app messages in the local queue will be consumed and removed from this queue. To keep in-app messages around after they are shown, override the default behavior (as described above).
Swift
// Get the in-app messages list
let messages = IterableAPI.inAppManager.getMessages()
// Show an in-app message
IterableAPI.inAppManager.show(message: message)
// Show an in-app message without consuming, i.e., not removing it from the queue
IterableAPI.inAppManager.show(message: message, consume: false)
Objective-C
// Get the in-app messages list
NSArray *messages = [IterableAPI.inAppManager getMessages];
// Show an in-app message
[IterableAPI.inAppManager showMessage:message];
// Show an in-app message without consuming, i.e., not removing it from the queue
[IterableAPI.inAppManager showMessage:message consume:NO callbackBlock:nil];
The SDK handles in-app message buttons and links as follows:
-
If the URL of the button or link uses the
action://
URL scheme, the SDK passes the action toIterableConfig.customActionDelegate.handle()
. IfcustomActionDelegate
(anIterableCustomActionDelegate
object) has not been set, the action will not be handled.- For the time being, the SDK will treat
itbl://
URLs the same way asaction://
URLs. However, this behavior will eventually be deprecated (timeline TBD), so it's best to migrate to theaction://
URL scheme as it's possible to do so.
- For the time being, the SDK will treat
-
The
iterable://
URL scheme is reserved for action names predefined by the SDK. If the URL of the button or link uses aniterable://
URL known to the SDK, it will be handled automatically and will not be passed to the custom action handler.- The SDK does not yet recognize any
iterable://
actions, but may do so in the future.
- The SDK does not yet recognize any
-
The SDK passes all other URLs to
IterableConfig.urlDelegate.handle()
. IfurlDelegate
(anIterableUrlDelegate
object) has not been set, or if it returnsfalse
for the provided URL, the URL will be opened by the system (using a web browser or other application, as applicable).
Take a look at this sample code
for a demonstration of how to implement and use the IterableURLDelegate
and IterableCustomActionDelegate
protocols.
The following code demonstrates how to assign a urlDelegate
and
customActionDelegate
to an IterableConfig
object:
let config = IterableConfig()
config.urlDelegate = YourCustomUrlDelegate()
config.customActionDelegate = YourCustomActionDelegate()
To customize the time delay between successive in-app messages (default value of 30 seconds), set IterableConfig.inAppDisplayInterval
to an appropriate value (in seconds).
To track custom events, use the following methods on IterableAPI
:
track(event:)
track(event:dataFields:)
track(event:dataFields:onSuccess:onFailure:)
To update an Iterable's user profile fields, use the following methods on
IterableAPI
:
updateUser(_:mergeNestedObjects:onSuccess:onFailure:)
updateEmail(_:onSuccess:onFailure:)
updateSubscriptions(_:unsubscribedChannelIds:unsubscribedMessageTypeIds:)
Iterable will track uninstalls with no additional work by you.
To do this, Iterable sends a silent push notification some time (currently, 12 hours) after a campaign has been sent. Based on this silent push notification, if Iterable receives feedback that the device token is no longer valid, it assigns an uninstall to the device based on the prior campaign. Similarly, if a "real" campaign uncovers an invalid device token, it will also check for a prior (within 12 hours) campaign to mark as the cause for the uninstall. If there was no recent campaign, Iterable still tracks the uninstall, but does not attribute it to a campaign.
For more information, take a look at:
- Iterable's iOS SDK Release Notes
- Iterable's Setting Up iOS Push Notifications guide
- Iterable's Push Notification Setup FAQs
The MIT License
See LICENSE
This library is open source, and we will look at pull requests!
See CONTRIBUTING for more information.