Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Let's get going!
The very first step is to go to Batch's dashboard and create a new iOS app.
Once your app is created, you will need to download a .p8 file and upload it to Batch's Dashboard.
As a reminder, Batch servers need to have a valid certificate in order to communicate with Apple Push Notification Services (APNS). There are two types of files you can use:
.p8 files (recommended) : Valid for all the apps added to your Apple developer account. You will need to specify the Application Identifier (App ID) or the Bundle ID of your app on Batch's dashboard.
.p12 certificates: Generated for a unique App ID and are only valid for one year. If you need a .p12 certificate, please follow these instructions: .
Step 1. Downloading the .p8 file
Head to the , then go to the "Keys" menu and add a new key.
Give a name to your key, tick the "Apple Push Notifications Service" box (1) and press "Configure" (2).
There, in the "Environment" dropdown, select "Sandbox & Production".
Click "Save" and continue until you can download the P8 key. Write down your Key ID.
Step 2. Uploading the .p8 file
Now go to Batch's dashboard → ⚙ Settings → Push settings, and upload your .p8 file.
There are two IDs you need to find to save your certificate and send notifications to your app:
App ID / Bundle ID: We recommend you use the bundle ID you will find in Xcode. You can also use the app ID available from the Developer Console .
Team ID: The team ID is also available from the Developer Console . It is also shown under your name in the top bar of the place you created your p8 key.
Key ID: The Key ID should be automatically filled if you didn't rename the p8 file you downloaded from Apple. If it was not, use the Key ID you have written down earlier. You can also find the Key ID in the Apple Developer Member center, where you generated it.
As Batch uses open-source software, you must include attribution text in your application. This usually goes in a "Legal Notices" or "About" screen.
You can get the attribution text .





Batch allows you to set a custom region or a custom language that will override the value detected by the SDK. By default, Batch collects the language and the country of your users' devices.
Setting a custom region/language is useful if:
You don't want to use the values detected by Batch.
Your users can choose their own language or region for localization purposes.
Writing custom region/language
Reading custom region/language
Keep in mind that the data returned is only about your installation and not your Profile since it may be updated from another source.
To make it easier to collect data to a Profile, Batch has added two automatic ways to migrate old installation's data on a Profile:
User will be automatically identified (logged-in) if he had a Batch custom_user_id set on the local storage.
User natives (language/region) and custom data will be automatically migrated to a Profile.
These migrations are triggered when your application on the dashboard is attached to a Project. Concretely this mean that there's two possible scenarios:
BatchProfile.editor { editor in
try? editor.setLanguage("en") // Language must be 2 chars, lowercase, ISO 639 formatted
try? editor.setRegion("US") // Region must be 2 chars, uppercase, ISO 3166 formatted
// If you prefer to use BatchProfile.editor() with local variable instead of usiong a closure,
// remember to use editor.save() afterwards so that the changes are taken into account.
}
[BatchProfile editWithBlock:^(BatchProfileEditor * _Nonnull editor) {
[editor setRegion:@"en" error:nil]; // Language must be 2 chars, lowercase, ISO 639 formatted
[editor setRegion:@"US" error: nil]; // Region must be 2 chars, uppercase, ISO 3166 formatted
// If you prefer to use [BatchProfile editor] instead of `editWithBlock`,
// remember to use [editor save] afterwards so that the changes are taken into account.
}];// This is how you retrieve your custom values. Values are nil by default.
let language = BatchUser.language()
let region = BatchUser.region()// This is how you retrieve your custom values. Values are nil by default.
NSString *language = [BatchUser language];
NSString *region = [BatchUser region];Your app is running on the SDK v1 and is already attached to a Project : migrations will be triggered on the first start running on SDK v2.
Your app is running on the SDK v2 but not attached to a Project: migrations will be triggered on the first start after your app is attached to a project.
These migrations are enabled by default, but you may want to disable them, to do so add the following before starting the SDK:
import Batch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Disable profile's migration
BatchSDK.setDisabledMigrations([
// Whether Batch should automatically identify logged-in user when running the SDK for the first time.
// This mean user with a custom_user_id will be automatically attached a to a Profile and could be targeted within a Project scope.
.customID,
// Whether Batch should automatically attach current installation's data (language/region/customDataAttributes...)
// to the User's Profile when running the SDK for the first time.
.customData
])
// Then start Batch SDK.
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
}@import Batch;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Disable profile's migration
[BatchSDK setDisabledMigrations:
// Whether Batch should automatically identify logged-in user when running the SDK for the first time.
// This mean user with a custom_user_id will be automatically attached a to a Profile and could be targeted within a Project scope.
BatchMigrationCustomID |
// Whether Batch should automatically attach current installation's data (language/region/customDataAttributes...)
// to the User's Profile when running the SDK for the first time.
BatchMigrationCustomData
];
// Then start Batch SDK.
[BatchSDK startWithAPIKey:@"MY_API_KEY"];
return YES;
}Batch provides a simple debug tool that allows you to test your custom data integration (⚙ Settings → Debug). It shows all the data available for a specific user ID:
Token: Find the token of a specific device.
Custom user ID: Check if your custom user ID implementation is working correctly.
Native attributes: See the installation date, last session date, country and more information on a specific user / install.
Custom data: See if your app is tagged properly and if custom attributes/events are sent to Batch.
Installation ID
You can find the Installation ID of a device by calling:
You can either log this to the debug logs, or display it in a debug menu directly in your app. It can safely be exposed to your end users, who can then give it back to you if they are experiencing push trouble with their app.
Starting with Batch 1.18, the SDK will also automatically copy the installation ID to the clipboard if a user follows a .
IDFA
You can use to find your Advertising Identifier (IDFA). Just install the app on your device, tap on the share button and copy the Advertising identifier.
The results displayed in the debug tool are updated in real time. There are five different pieces of information shown for a single ID:
Basic information (token, install ID, custom user ID, etc).
prefixed with b. (b.attribute_name).
prefixed with c. (c.attribute_name).
Here are some details on the native attributes you will find in the results:
b.app_version: Version of the app installed on your user's device.
b.carrier_code: (Not supported on iOS 16.4+) Your user's carrier. The code provided by Batch is a combination of the Mobile country code (MCC) and the Mobile network code (MNC). The full list of MCC and MNC codes is .
b.city_code: City detected by Batch using IP geolocation.
In this article, we'll go through the process of improving your external analytics by either installing a premade event dispatcher or making your own one.
Batch can trigger with a number of analytics-oriented events that might be of interest in external analytic tools. An event dispatcher is the code that listens to those events, and dispatches them to the solution of your choice.
Batch provide you with premade dispatchers for specific use cases (aka ready-to-go dispatcher). They are enabled by simply registering them in your AppDelegate, and will automatically track events into your analytics solution with a predetermined structure.
Or, if you need, you can implement your own dispatcher.
The Batch SDK will inform you when one of the following event happen:
Push notification displayed : Triggered when a push notification is displayed (only available on Android).
Push notification clicked : Triggered when a push notification is clicked.
Push notification dismissed : Triggered when a push notification is dismissed (only available on Android).
Message showed : Triggered when an in-app or landing message appear on the screen.
With every event comes a BatchEventDispatcherPayload object, allowing you to access data associated with the event, such as a push notification custom payload value, or the tracking ID of an in-app or landing campaign.
The Android Batch SDK allows you to:
Add and remove phone number from a user profile. The profile will automatically be created if needed.
Edit a profile's marketing subscription for the SMS channel.
Here is how to set a phone number with a marketing subscription:
Note:
The phone number must be an formatted string starting with a
+and not longer than 15 digits without any special characters. It should match the following regular expression :^\+[0-9]{1,15}$Remember if you call
editormethod beforestartWithAPIKeyit will return nil. You should always call it after you started the SDK, and check nullity to be safe.
Batch attaches SDK's installations to a Profile and centralize data and events from multiple sources (Apps, Websites, APIs) in a single place based on a Custom user ID. This allows Batch to support more complex and cross-channel lifecycles: it is now possible to message a user on a channel even though its profile data is fed from another one.
This allows you to use the .
This custom user identifier can be:
The unique ID you are using in your login system.
A stable ID used in your data store (e.g. Firebase, etc).
URLs present in notifications and mobile landings/in-app messages are handled by the Batch SDK in the following ways:
the default implementation by the SDK will automatically call -[UIApplication openURL:] with any URL it encounters
alternatively you can use a BatchDeeplinkDelegate to handle the URL your way
The iOS Batch SDK allows you to:
Add and remove email address from a user profile. The profile will automatically be created if needed.
Edit a profile's marketing subscription.
// This requires to have a custom user ID registered by calling the `identify` method beforehand.
BatchProfile.editor { editor in
try? editor.setPhoneNumber("+33123456789") // Nil to erase. A valid E.164 phone number.
editor.setSMSMarketingSubscriptionState(.subscribed) // or .unsubscribed
// If you prefer to use BatchProfile.editor() with local variable instead of using a closure,
// remember to use editor.save() afterwards so that the changes are taken into account.
}// This requires to have a custom user ID registered by calling the `identify` method beforehand.
[BatchProfile editWithBlock:^(BatchProfileEditor * _Nonnull editor) {
NSError *error = nil;
[editor setPhoneNumber:@"+33123456789" error:&error]; // Nil to erase. A valid E.164 phone number.
[editor setSMSMarketingSubscriptionState:BatchSMSSubscriptionStateSubscribed]; // or BatchSMSSubscriptionStateUnsubscribed
// If you prefer to use [BatchProfile editor] instead of `editWithBlock`,
// remember to use [editor save] afterwards so that the changes are taken into account.
}];Any stable information that can help you to identify a user (e.g. hashed email address, etc).
If you want to only push a specific installation (and thus a specific device), you should set an installation-specific identifier or use Batch installation ID.
The API is pretty straightforward and MUST be called when Batch is started.
You should remove the identifier anytime the identified user logs out.
User identifiers are case and whitespace sensitive. If you want to avoid potential issues, you may want to call lowercaseString and stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] on the identifier string.
User identifiers must be shorter than 1024 characters.
Make sure the identifier is unique, otherwise users might get pushes that are not meant for them. You should never associate a custom ID to more than one user.
If you are wondering which ID you should use as a custom user identifier, here are a few examples:
Login system
If you have a login system, you probably have a unique user ID associated to the user's account. Simply set this ID as your user identifier, and don't forget to reset it (by setting it to nil) when the user logs out.
While email addresses are an appropriate identifier, we suggest you hash them before setting them as your identifier, for privacy reasons.
PaaS datastore
Your datastore probably has a stable ID associated to your installation. You just need to make sure it is unique between installations, and you are easily able to retreive it from your backend code.
Specific backend and database
Anything that can identify a user uniquely will work. Email address, phone number, etc. We suggest you hash this information before setting it, and use the same hash on your backend.
Otherwise, you will need to create a stable identifier for your app installation and send it to your backend. The most common way to do that is to generate a RFC4122 UUID.

Tags prefixed with t. (t.tag_name).
Events prefixed with e.(e.event_name).
b.device_brand: Brand of your user's device.
b.device_type: Model of your user's device. See here if you need more information on a specific iOS/Android device model.
b.install_date: Install date of the app (UTC/GMT).
b.language: Language of your user.
b.last_push_date: Date of the last sent notification from the dashboard or the Campaigns API (UTC/GMT).
b.last_visit_date: Date of the last session (UTC/GMT).
b.os_version: Version of the OS used on your user's device.
b.region: Country of your user.
b.transaction_tracked: Your user has or hasn't made any purchases in your app. Make sure you are sending data on transactions to see this information.
b.is_push_optin: True if the install is opt-in to push, meaning that it can receive and display push notifications

Message closed : Triggered when an in-app or landing message is explicitly closed by the user (using the close button or a dismiss CTA/Global tap).
Message auto-closed : Triggered when an in-app or landing message is closed by the auto-close timer.
Message clicked : Triggered when an in-app or landing Click-To-Action is clicked or when the in-app/landing is global tapped (except if the CTA/Global Tap is a dismiss, then a closed event is triggered).
BatchProfile.identify("custom_user")[BatchProfile identify:@"custom_user°id"];BatchProfile.identify(nil)[BatchProfile identify:nil];BatchUser.installationID()[BatchUser installationID];func dispatchEvent(with type: BatchEventDispatcherType,
payload: BatchEventDispatcherPayload) {
NSLog("Dispatcher: I'm dispatching an event with ID: %@", payload.trackingId);
}- (void)dispatchEventWithType:(BatchEventDispatcherType)type
payload:(nonnull id<BatchEventDispatcherPayload>)payload {
NSLog(@"Dispatcher: I'm dispatching an event with ID: %@", payload.trackingId);
}arm64 (both for iOS, and iOS Simulator on Apple Silicon)
Catalyst is supported.
The iOS SDK also supports visionOS in a restricted usage since:
In-App messaging and mobile landings are unavailable.
In-App rating is not supported on visionOS due to an OS limitation.
Integrating with:
Use Xcode's Swift Package Manager wizard to add
https://www.github.com/BatchLabs/Batch-iOS-SDK.gitCocoaPods 1.15 required
First, simply add this line to your Podfile to integrate Batch in your project:
pod 'Batch', '~> 3.1'Then, run pod install in your Podfile folder, and open the .xcworkspace that was created. You're ready to go! In order to update Batch SDK, simply run pod update in that directory.
If you don't have a Podfile or are unsure on how to proceed, see the CocoaPods usage guide.
Note: Due to CocoaPods limitations, the Batch Pod is not a mergable library and has no code signing. If you need one of those, please use SPM or manually integrate the XCFramework.
Batch is only available on Carthage 0.30 and higher
Simply add this line to your Cartfile to integrate Batch in your project:
Do not add Batch to the "carthage copy-frameworks" script input/output.
XCFramework distribution is supported since Batch 1.18.0 and Carthage 0.38.
Apple now requires Privacy Manifest that's not available with Carthage.
Implement the Batch startWithAPIKey: method in your AppDelegate application:didFinishLaunchingWithOptions: method:
If you're making a SwiftUI app, you will need to add a delegate first.
YOUR_API_KEY is your SDK API Key. You'll find it in ⚙ Settings → General.
Congratulations on finishing the bulk of the integration!
If you want to validate your implementation before proceeding with the Push setup, you can locate the log that Batch posts in the Xcode console.
Batch also provides a simple debug tool that allows you to test your integration (⚙ Settings → Debug) and a profile view that shows all the data available for a specific user ID.
The SDK references your delegate weakly so make sure you hold a strong reference to it. A good idea could be to use your application delegate as the deeplink delegate.
Universal links let you connect to content deep inside your app using standard HTTP or HTTPS links (see documentation on the Apple Developper website).
As Universal links cannot be opened from within an app that supports them using UIApplication.openURL, some extra configuration is needed.
1.18.0 Once you have configured your app to handle universal links, you will need to declare your associated domains to the Batch SDK like in your Associated Domains Entitlement file.
class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchSDK.associatedDomains = ["example.com"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[BatchSDK setAssociatedDomains:@[@"example.com", @"www.example.com"
For older SDK versions, you will have to use a BatchDeeplinkDelegate to handle these URLs manually.
Custom URL schemes, like customScheme://first-page, provide a way to reference resources inside your app (see documentation on the Apple Developer website).
This type of URL is properly handled without a deeplink delegate. However, if you find yourself using a deeplink delegate, you will need to pass your custom-scheme URL manually to -[UIApplication openURL:]. This is a necessary step, considering setting a deeplink delegate overrides the default implementation of the SDK.
let delegate: BatchDeeplinkDelegate
// deeplinkDelegate is a class property of the Batch class
BatchSDK.deeplinkDelegate = delegate
// Then make your delegate implement the only function from the BatchDeeplinkDelegate protocol
func openBatchDeeplink(_ deeplink: String)id<BatchDeeplinkDelegate> delegate;
// deeplinkDelegate is a class property of the Batch class
BatchSDK.deeplinkDelegate = delegate;
// Then make your delegate implement the only method from the BatchDeeplinkDelegate protocol
- (void)openBatchDeeplink:(nonnull NSString*)deeplink;Here is how to set an email with a marketing subscription:
// This requires to have a custom user ID registered by calling the `identify` method beforehand.
BatchProfile.editor { editor in
try? editor.setEmailAddress("john.doe@batch.com") // Nil to erase. Addresses must be valid.
editor.setEmailMarketingSubscriptionState(.subscribed) // or .unsubscribed
// If you prefer to use BatchProfile.editor() with local variable instead of using a closure,
// remember to use editor.save() afterwards so that the changes are taken into account.
// This requires to have a custom user ID registered by calling the `identify` method beforehand.
[BatchProfile editWithBlock:^(BatchProfileEditor * _Nonnull editor) {
NSError *error = nil;
[editor setEmailAddress:@"john.doe@batch.com" error:&error]; // Nil to erase. Addresses must be valid.
[editor setEmailMarketingSubscriptionState:BatchEmailSubscriptionStateSubscribed];
If you call editor method before startWithAPIKey it will return nil. You should always call it after you started the SDK, and check nullity to be safe.
Batch 1.16 comes with a number of changes regarding the distribution of the SDK and it's extension counterpart.
This migration guide will help you update your implementation.
Batch 1.16 requires iOS 10.0 or higher. If your app targets an older version of iOS, please downgrade to 1.15.
With the 1.16 release, BatchExtension has been rewritten and open-source in two repositories:
(Swift version, recommended)
(Objective-C version, should only be used if you cannot use the Swift one)
This marks the end of the binary distribution of BatchExtension, which used to be bundled with the SDK downloads. The only way to integrate BatchExtension in your application from now on is using the source. Fortunately, package managers make this easy to deal with!
Migrating to the new BatchExtension is done in three steps:
Remove the old BatchExtension framework
Add the new one using a package manager
Update your code to match the new API signatures, if needed.
Since BatchExtension has never been distributed on a package manager, you had to copy it in your project and add a script phase.
In order to remove the framework, simply find it in the Project Navigator (which is the first tab of the left sidebar), and delete it:
When asked what to do with the deletion, pick Move To Trash.
Xcode should have automatically removed the framework reference from the Link Binary With Libraries and Embed Frameworks build steps.
Now, go in your main target's Build Steps and find the Run Script phase that refences a script which path contains BatchExtension.framework/strip-frameworks.sh. Be careful, as you might have multiple script phases.
Delete it.
BatchExtension has now been fully removed from your project. Now, we have to add it back!
Note: If your extension code is written in Objective-C and uses BAENotificationServiceExtension, please use the Objective-C version of the extension.
The extension is available on Swift Package Manager, CocoaPods and Carthage.
Please which will walk you through integrating the extension, and come back here once done.
If the extension builds without issues, you do not have anything to do!
If it doesn't, what you have to do depends on how you integrated the extension and which language you use:
You're using Swift code
BatchExtension 3.0.0 introduces the following source breaking changes for Swift users:
Classes have been unprefixed (except BAENotificationServiceExtension). For example, if your Swift code references BAERichNotificationHelper, you need to change it to RichNotificationHelper.
RichNotificationHelper.appendRichData(to: completionHandler:) has been renamed. It is now RichNotificationHelper.appendRichData(toContent: completionHandler:). Xcode offers a quickfix for this.
You're using Objective-C code
While the public API doesn't change, you might run into the following message: Cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute.
This happens if you used the standard version of the extension using Swift PM.
To fix this, use the Objective-C version of the extension as described in the , or rewrite the extension code in Swift (do not forget to update the extension's Info.plist with the new class name).
If you run into any other issue, please contact our support team to get help on your migration.
Batch 1.16 is now distributed as a static XCFramework instead of a legacy fat static framework. While the legacy framework format is still distributed, we encourage you to switch to the XCFramework build as soon as possible.
Batch 1.16.0 and higher are distributed as a XCFramework in CocoaPods. CocoaPods should handle the transition without needing any action on your end.
If you're running into issues, make sure you've updated to the latest CocoaPods version for XCFrameworks support.
As of writing, Carthage (ver 0.36.0) does not support XCFrameworks. Therefore, Batch is still distributed as a fat framework for Carthage.
iOS Simulator for Apple Silicon and Mac Catalyst support are not available with carthage.
We've heard loud and clear that developers would like to use Swift Package Manager to add Batch in their app. Unfortunately due to bugs in Xcode 12's handling of binary frameworks in SPM packages, Batch isn't distributed there yet. We will reevaluate this decision when a new version of Xcode that fixes the issues is released.
.
Replacing your manual Batch integration with the XCFramework distribution is quite easy.
First, delete Batch.embeddedframework (or Batch.framework) in the Project Navigator. Xcode should remove all of the Framework's reference in the build steps. Pick Move To Trash when asked.
Then, drag & drop Batch.xcframework in the Project Navigator where the framework you deleted was.
Unless you manually copied the XCframework folder into your project's directory, check Copy items if needed. Make sure the only checked target is your Application target: extensions should be unchecked.
Xcode should have added Batch.xcframework in the Link Binary With Libraries build step. Add it manually if this did not happen.
Your project should now build!
The Mixpanel dispatcher automatically dispatches events to the Mixpanel SDK, using UTM tags when available.
Two flavors of the dispatcher are available, depending on which flavour of the Mixpanel SDK you're using:
One for the Objective-C Mixpanel SDK
One for the Swift Mixpanel SDK
"https://github.com/BatchLabs/Batch-iOS-mixpanel-swift-dispatcher"pod 'BatchMixpanelSwiftDispatcher'
# or
# pod 'BatchMixpanelObjcDispatcher'github "BatchLabs/Batch-iOS-mixpanel-swift-dispatcher"
# or
# github "BatchLabs/Batch-iOS-mixpanel-objc-dispatcher"Don't forget to register the dispatcher:
Once your dispatcher is installed, restart your app and you should see something like this in your log:
The Firebase dispatcher automatically dispatches events to the Firebase SDK, including UTM tags when they are available.
https://github.com/BatchLabs/Batch-iOS-firebase-dispatcher.gitpod 'BatchFirebaseDispatcher'github "BatchLabs/Batch-iOS-firebase-dispatcher"Don't forget to register the dispatcher:
@import BatchFirebaseDispatcher
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey
// MYAppDelegate.h
@interface MYAppDelegate : UIResponder <UIApplicationDelegate>
[...]
@end
// MYAppDelegate.m
@import BatchFirebaseDispatcher;
@implementation MYAppDelegate
If for some reason, you need to implement your own dispatcher, you must create a BatchEventDispatcherDelegate and register it to the SDK using [BatchEventDispatcher addDispatcher].
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, BatchEventDispatcherDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]
// MYAppDelegate.h
@interface MYAppDelegate : UIResponder <UIApplicationDelegate,
BatchEventDispatcherDelegate>
[...]
@end
// MYAppDelegate.m
@implementation MYAppDelegate
The AT Internet dispatcher automatically dispatches events to the AT Internet SDK, including the XTOR tag when it's available.
This dispatcher should not be used in new integrations in favor of Piano Analytics
github "BatchLabs/Batch-iOS-atinternet-dispatcher"pod 'BatchATInternetDispatcher'Then, register the dispatcher:
import BatchATInternetDispatcher
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey
// MYAppDelegate.h
@interface MYAppDelegate : UIResponder <UIApplicationDelegate>
[...]
@end
// MYAppDelegate.m
@import BatchATInternetDispatcher;
@implementation MYAppDelegate
Batch AT Internet Dispatcher 1.1.0+ will automatically instantiate Tracker instances using the default AT Internet configuration, which comes in a plist.
If you're not using plist-based configuration and would like to manually configure your Tracker in code, you can tell Batch which tracker instance to use:
If you plan to send personal user data and have to comply with EU's GDPR, any other data privacy law or simply want to give your users more control regarding their privacy, these methods will help you plug your consent screen to Batch.
There are two SDK operation modes:
SDK enabled by default, opting-out required via an explicit method call: this is the default mode.
SDK disabled by default, opting-in required
import Batch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
[..]
}@import Batch;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Start Batch SDK.
[BatchSDK startWithAPIKey:@"MY_API_KEY"];
return YES;
}import SwiftUI
import Batch
class MyAppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
return true
}
}
@main
struct BatchApp: App {
// Set an explicit delegate to integrate Batch in
@UIApplicationDelegateAdaptor(MyAppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}[Batch] Installation ID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX




github "BatchLabs/Batch-iOS-SDK"On iOS, disabling the SDK won't disable push notifications if Batch already has a valid token. In case you want to implement a notification opt-out, you will need to direct users to iOS' settings by using the [BatchPush openSystemNotificationSettings](Objective-C) or the BatchPush.openSystemNotificationSettings() (Swift) method.
In order to opt-out from SDK, simply call:
BatchSDK.optOut()[BatchSDK optOut];Opting out will:
Prevent [BatchSDK startWithAPIKey:] from starting the SDK
Disable any network capability from the SDK
Disable all In-App campaigns
Make the Inbox module return an error immediately
Make any call to -[BatchProfileEditor save] do nothing
Make any "tracking" methods from BatchProfile ineffective
Even if you opt-in afterwards, data generated (such as user data or tracked events) while opted out WILL be lost.
You can also wipe the SDK installation data:
This will wipe the data locally and request a remote data removal for the matching Installation ID and its associated data. Any user-level data associated with the Custom User ID (if any) set via our server APIs will not be deleted. However, the Installation ID <> Custom User ID association will be, effectively logging the user out before deleting their installation data. Sending notifications to this user using their Advertising ID (via the Transactional API or a Custom Audience) will not be possible for a month.
In order to disable the SDK by default, you need to add a key called BATCH_OPTED_OUT_BY_DEFAULT with a true boolean value to your Info.plist.
Once you've opted-out (either programatically, or by default), you can re-enable the SDK by calling the following:
You can disable Batch SDK by default and start it when you need it, later in the user journey. This is helpful if you are using a Consent Management Platform (CMP) and don't want to start collecting data before obtaining your users' consent.
Batch will only detect in the analytics and be able to contact users who started the SDK once. The rest of your users won't be detected by Batch. You won't be able to reach them using push notifications or In-App messages.
If you want to give your users more control regarding their privacy, you can give them the option to disable Batch SDK in the app settings.
A) Disabling the SDK
Batch SDK will not start anymore. Here are the impacts of the SDK deactivation:
Analytics: Your users will no longer be detected by Batch when they open the app or open a notification.
Data collection: Batch will stop collecting native and custom data for these installs.
Push notifications: iOS users opt-in to push notifications will keep receiving alerts because they are displayed directly by the OS and their push token remains valid even after Batch SDK opt-out. They will need to disable push notifications manually from iOS system settings if they want to stop receiving alerts. On Android, Batch SDK receiver is in charge of receiving and displaying push notifications. Disabling Batch SDK will also disable push notifications.
In-App messaging: Your users won't see any In-App messages. In-App messages are displayed by Batch SDK.
Inbox: The content of your notification centre won't be updated if you rely on Batch Inbox.
If users re-enable Batch SDK later from your app settings, analytics, data collection, push notifications, In-App messages and the Inbox feature will work again.
B) Disabling the SDK and wiping the data
In addition to disabling the SDK, you can also send a request to delete the data attached to the installation. This will wipe the data locally and request a remote data removal for the matching Installation ID. You should only use that method if you are fully aware of the impacts on your users.
Batch will blacklist the advertising ID attached to the installation for one month following the data removal.
import Batch
import BatchMixpanelSwiftDispatcher
// or import BatchMixpanelObjcDispatcher
import Mixpanel
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Find your mixpanel setup line, which looks like this:
let mixpanel = Mixpanel.initialize(token: "MIXPANEL_TOKEN")
// Objective-C Mixpanel SDK users: let mixpanel = Mixpanel.sharedInstance(withToken: "MIXPANEL_TOKEN")
let mixpanelDispatcher = BatchMixpanelSwiftDispatcher.instance
// If you're using BatchMixpanelObjcDispatcher, comment the previous lines and uncomment the next one
// let mixpanelDispatcher = BatchMixpanelObjcDispatcher.instance()
// Tell Batch about your Mixpanel instance to enable tracking
mixpanelDispatcher.mixpanelInstance = mixpanel
BatchEventDispatcher.add(mixpanelDispatcher)
[...]
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
return true
}
}The Piano Analytics dispatcher automatically dispatches events to the Piano SDK, including AT tags when they are available.
https://github.com/BatchLabs/Batch-iOS-piano-dispatcher.gitpod 'BatchPianoDispatcher'github "BatchLabs/Batch-iOS-piano-dispatcher"By default the dispatcher will handle UTM tracking and will send events only as Piano On-site Ads events.
Register the dispatcher and update the configuration as following:
BatchSDK.optOutAndWipeData()[BatchSDK optOutAndWipeData];BatchSDK.optIn()
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
BatchPush.refreshToken();[BatchSDK optIn];
[BatchSDK startWithAPIKey:@"YOUR_API_KEY"];
[BatchPush refreshToken];// MYAppDelegate.m
@import Batch;
@import BatchMixpanelObjcDispatcher;
@import Mixpanel;
@implementation MYAppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Find your mixpanel setup line, which looks like this:
Mixpanel *mixpanel = [Mixpanel sharedInstanceWithToken:@"MIXPANEL_TOKEN"];
// BatchMixpanelSwiftDispatcher isn't supported from Objective-C
BatchMixpanelObjcDispatcher *mixpanelDispatcher = [BatchMixpanelObjcDispatcher instance];
// Tell Batch about your Mixpanel instance to enable tracking
mixpanelDispatcher.mixpanelInstance = mixpanel;
[BatchEventDispatcher addDispatcher:mixpanelDispatcher];
[...]
[BatchSDK startWithAPIKey:@"MY_API_KEY"];
return YES;
}
@end[Batch] EventDispatcher - Adding event dispatcher: BatchFirebaseDispatcher// Put this right after BatchEventDispatcher.add(BatchATInternetDispatcher.instance())
let tracker = <your ATInternet tracker initialization code>
BatchATInternetDispatcher.instance().trackerOverride = tracker// Put this right after [BatchEventDispatcher addDispatcher:[BatchATInternetDispatcher instance]];
Tracker *myTracker = ...;
[[BatchATInternetDispatcher instance].trackerOverride = myTracker;@import BatchPianoDispatcher
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let pianoDispatcher = BatchPianoDispatcher.instance
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// PA Configuration
pa.setConfiguration(ConfigurationBuilder()
.withCollectDomain("logsx.xiti.com")
.withSite(123456789)
.build()
)
// Uncomment if you want to enable custom event sending (default: false)
//pianoDispatcher.enableCustomEvents = true
// Uncomment if you want to disable On-Site Ads event sending (default: true)
//pianoDispatcher.enableOnSiteAdsEvents = false
// Uncomment if you want to disable UTM tracking (default: true)
//pianoDispatcher.enableUTMTracking = false
BatchEventDispatcher.add(pianoDispatcher)
[...]
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
return true
}
}Inbox is an optional feature. If you are interested, please contact us.
The Inbox API allows you to fetch and process notifications that this user has previously received, even if their device was offline. You can then do anything you want with the data, such as making a "notification center", where the user can catch up with previous notifications in a list.
The API gives you access to the entire notification, including its raw payload. It also lets you know if the notification has already been read, and allows you to mark one or all notifications as such. Once received/stored in the inbox, your push notifications will remain for a 3 months period.
This screenshot is an example of what you can achieve with the Inbox API. Please note Batch does not include any UI.
This mode will fetch notifications from the current app installation, and nothing else. If the user clears the app's data, this inbox will be cleared. This is great for applications where your users don't have to log in.
In this mode, you can simply get the Inbox Fetcher with a context:
This mode will fetch notifications for the specified , even if they just installed the application and already got notifications on another device logged in with the same user identifier. If your application has a login system, this is the mode you should use.
Since notifications can have sensitive content, you cannot get a user's notifications simply with their user identifier: Batch requires you to authenticate your request.
Getting Your Authentication Key
First, you will need your inbox secret key. You will find that in your dashboard, below your API Keys. It is unique for every app.
The authentication key is generated by computing a sha256 hmac hash of the API Key concatenated with the user identifier, using the secret as the key. Then, you have to encode the hash in a hexadecimal string.
For the API Key "abcdef", user identifier "paul" and secret of "foobar", the string to hash is abcdefpaul and the expected authentication key would be 796f1ab5d119d1b2eab8201e60335b56d1befff40c0f80263d64a169a8fd2e45.
PHP example code:
Note: This hash HAS to be computed on your server. If you bundle the inbox secret in your application to compute the hash, attackers will be able to extract it, and read the notifications of any of your users
Getting the Fetcher Instance
Once you've got the authentication key from the server, you only have to give Batch the right user identifier and auth key tuple to get the Inbox Fetcher:
Now that you've got your fetcher instance, it's time to fetch notifications, and display them!
The inbox fetcher has several important methods and properties:
allFetchedNotifications
This returns a copy of all the notifications that have been fetched. Useful for a list adapter.
Warning: Calling this always makes a copy of the objects, so you should cache that method's result.
fetchNewNotifications()
Allows you to fetch notifications that might have been received after the initial fetch. This is useful for implementing a refresh feature. This will erase the notification history returned by allFetchedNotifications to ensure consistency: you should clear the other notification pages you've already from your cache.
BatchInboxFetcher will not fetch any notification by default, so before trying to display anything, you will need to call fetchNewNotifications() or fetchNextPage()
Both fetch methods take a block, which the SDK will call you back on either on failure, or success. Success callbacks include information about the operation to operate on the data, but you can very well do with the global methods. The block callbacks are always called on the main thread.
Once you've fetched notifications, you will end up with a list of BatchInboxNotificationContent objects.
These objects have everything you need to display these notifications:
Title
Body
Send timestamp (UTC)
Read state
Note: Just like when you get it using iOS' standard callbacks, the raw payload (accessible from the payload property) should be used carefully on keys you do not control:
"aps" is considered private by Apple, and can change at any time. This has happened in the past, where the "alert" object got additional keys for new iOS features.
"com.batch" is Batch's internal payload: You should not make any assumption about its content, nor depend on it. We reserve the right to change it at anytime, without warning.
Standard parsing good practices apply: Make sure to check every cast and handle errors gracefully, and never assume anything about the payload's content.
You can then use the same methods that you can use on push payloads to extract information. Example: BatchPush.deeplink(fromUserInfo: notificationContent.payload), where "notificationContent" is a BatchInboxNotificationContent instance.
Notifications can be marked as read in two ways:
By marking only one notification as read
Use markNotification(asRead: notification) with the notification you want to mark as read.
By marking all notifications as read
Simply call markAllNotificationsAsRead()
In both cases, the notifications will be marked as read locally, but refreshing them might mark them as unread again, as the server might not have processed the request. These methods return quickly, and are thus safe to call on your UI thread.
Note that notifications that have been opened when received are automatically marked as read.
Batch allows you to display a mobile landing attached to a notification. To do so, you can simply trigger the landing message from the BatchInboxNotificationContent object as following:
Notifications can be deleted using the markNotification(asDeleted: notification) method with the notification you want to mark as deleted. A deleted notification will NOT appear in the notification list the SDK provides, and you will be expected to update your UI accordingly.
The notifications will be marked as deleted locally, but refreshing them might make them appear again, as the server might not have processed the request. These methods return quickly, and are thus safe to call on your UI thread.
For more advanced usages, you can also change various aspects of the Inbox Fetcher, such as:
The maximum number of notifications fetched per page (maxPageSize)
The maximum number of notifications that can be fetched using this object (limit) A default limit is set to avoid going over memory by accident.
1.19.0 Disabling the filtering of silent notifications (filterSilentNotifications). A silent notification is a push that doesn't show any visible message to the user.
In order to test your integration, you will need to create a push campaign on the dashboard. Please see our fore more info.
In order to make your SDK integration as easy as possible, Batch Push automatically integrates into your application delegate by using a technique called "method swizzling".
Even though we've taken the greatest care when writing our swizzling code, you may encounter some cases where you don't want that, such as:
Incompatibility with other SDKs that also try to integrate themselves
Incompatibility with third party app development solutions
Swizzling breaks your delegate's architecture
You don't want code swizzled on your behalf
That's why, starting with Batch 1.5.3, we support a fully manual integration of the SDK.
To disable automatic integration, simply call:
This must be done before [BatchSDK startWithAPIKey:].
Then, you'll have to put calls to BatchPush where needed, in order to ensure that all Batch Push functionality works. Not implementing any of them will cause issues.
Here's a sample app delegate:
If using Batch 1.15 or lower on an application that supports iOS 8 and 9, you need to implement some extra methods:
On iOS 10 and higher, you should also implement UNUserNotificationCenterDelegate. How to correctly integrate it with Batch does not change in manual mode, and is detailed in .
By default, the Batch SDK will automatically collect some data related to the user's device:
Device Model
The user's device model information
false
Installation ID
The installation identifier
true
Session ID
The user's session identifier
Some of this data are disabled by default and can be toggled on/off according to the user consent:
Device model
GeoIP
Use the following API to fine-tune what you want to enable:
In-App Campaigns allow you to trigger messages when users open your app or perform a specific action. This is great to communicate with users who have turned off push notifications or to show contextual messages while your users are browsing your app (e.g. special offers, update reminder, etc).
Batch allows you to customize your notifications adding specific keys to the payload.
Since we allow the overriding of Apple's keys, you can use and override anything Apple accepts in the aps key. Apple's full documentation about the payload is
All of the following JSON examples go into the custom_payload field of both Transactional and Campaigns APIs.
You can add a badge to your iOS notifications by adding a badge
There is no code required to make In-App Messages work in automatic mode. Create some campaigns on your dashboard, and they will start coming up in your app. If they don't, please head over to the troubleshooting section.
Disabling Dynamic Type
Batch adapts textual content to honor the user's font size settings. This is enabled by default, you can disable it at any moment by using:
You can also get more control on when messages are displayed without giving up on the automatic mode, by using the "Do Not Disturb" (DnD) mode. It allows you to tell Batch to hold on a mobile landing for you, rather than display it without using the fully manual mode. For example, if launching your app results in a splash screen or a fullscreen ad, you might find it undesirable to have Batch display something on top of it.
Turning on "Do Not Disturb" mode will make Batch enqueue the latest mobile landing, rather than display it.
Toggling DnD
Now, when you don't want Batch to automatically display, turn on Do Not Disturb:
Once you want to start showing landings automatically, call the method with false to turn it off.
Displaying pending mobile landings
After coming back from DnD mode, you might want to show the enqueued message, as Batch will not do that automatically. Batch exposes two properties/methods for managing the queue:
BatchMessaging.hasPendingMessage , allowing you to peek into the queue.
BatchMessaging.popPendingMessage() , allowing you to fetch the pending message (if any). Since calling this makes Batch delete its reference to it to save memory, further calls might return nil.
BatchMessaging.showPendingMessage() , allowing you to try to show the pending message, if any.
Here is a quick example of how they can be used:
Like Mobile Landings, you may want to take full control over how in-app messaging behaves. Batch allows you to disable automatic displaying, and handle that yourself. For compatibility reasons, BatchMessaging.setAutomaticMode(false) does not control this behavior like it does on mobile landings.
In order to handle messages yourself, you will have to set an implementation of the BatchInAppDelegate protocol. By implementing this, you opt-in to manual message handling, and showing them becomes your responsibility.
Here is an example:
This can come from multiple things:
Batch might not be able to display the message: make sure all of the prerequisites are satisfied by your app. If you have a non standard UI architecture.
The In-App campaign might not have been synchronized yet. Try to kill and restart your app: backgrounding it might not be enough.
If the trigger is "next session", you might just not have triggered a new session yet. Try killing (as in swiping it up on the task manager) and restarting the app, or wait 5 minutes with the app in the background. Restarting the app might be needed twice: once to sync the campaigns, and one to trigger the "next session" event.
If an In-App message is triggered while a view controller is being dismissed (especially a modally presented one), showing the message can fail. You can try displaying it manually, or tracking the event that triggers an in-app message after the dismiss animation.
If that does not fix the problem, please check the logs in Xcode/Console.app and see if Batch or UIKit outputs anything. If not, you can always contact our support team with information about your problem.

fetchNextPage()endReached Lets you know if more notifications might be available. Use this to make an infinite list, or a "load more" button.
Rich notification attachment URL
Raw payload (like userInfo in Apple's callbacks)


BatchPush.disableAutomaticIntegration()[BatchPush disableAutomaticIntegration];func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchPush.disableAutomaticIntegration()
BatchSDK.start(withAPIKey: "YOUR API KEY")
// You MUST have a UNUserNotificationCenterDelegate implementation.
// Batch provides BatchUNUserNotificationCenterDelegate as a default one: if you have your own, integrate Batch into it.
// See "Intercepting notifications" for more info.
// Available from Batch 1.16
BatchUNUserNotificationCenterDelegate.registerAsDelegate()
// Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
BatchPush.requestNotificationAuthorization()
// Alternatively, you can call requestNotificationAuthorization later
// But, you should always refresh your token on each application start
// This will make sure that even if your user's token changes, you still get notifications
// BatchPush.refreshToken();
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
BatchPush.handleDeviceToken(deviceToken)
}true
Custom User ID
The user's custom identifier
true
API Level
The Batch SDK api level
true
Messaging API Level
The Batch SDK Messaging api level
true
Device Language
The user's device language
true
Device Region
The user's device region
true
Device Timezone
The user's device timezone
true
OS Version
The user's device iOS version
true
App Version (String)
The application version (string)
true
App Version (Build Number)
The application version (number)
true
Bundle Name
The application bundle identifier
true
Device Date
The current device date
true
Device Installation Date
The Batch installation date
true
Bridge Version
Version of the batch bridge (for cross-platform plugin only)
true
Plugin Version
Version of the batch plugin (for cross-platform plugin only)
true
GeoIP
Whether Batch should resolve the user's location from ip address
false
BatchMessaging.setEnableDynamicType(false)[BatchMessaging setEnableDynamicType:false];BatchMessaging.doNotDisturb = trueBatchMessaging.doNotDisturb = YES;func splashScreenDidDisappear() {
BatchMessaging.doNotDisturb = false
BatchMessaging.showPendingMessage()
}- (void)splashScreenDidDisappear {
BatchMessaging.doNotDisturb = NO;
[BatchMessaging showPendingMessage];
}
// Application delegate
var inAppMsgDelegate = InAppMsgDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.setInAppDelegate(inAppMsgDelegate)
}
// InAppMsgDelegate implementation
@objc
class InAppMsgDelegate: NSObject, BatchInAppDelegate {
func batchInAppMessageReady(message: BatchInAppMessage) {
// Sample implementation that prevents messages from being displayed if the logged in user isn't the right one
// "loggedInUsername" is an hypothetical property that returns the logged in username as a string, if logged in
if (UserManager.loggedInUsername == message.customPayload?["username"] as? String) {
do {
let vc = try BatchMessaging.loadViewController(for: message)
BatchMessaging.present(vc)
} catch let error as NSError {
// Handle the error
}
}
}
}
// Application delegate
@property InAppMsgDelegate *inAppMsgDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.inAppMsgDelegate = [InAppMsgDelegate new];
[BatchMessaging setInAppDelegate:self.inAppMsgDelegate];
}
// InAppMsgDelegate implementation
@interface InAppMsgDelegate : NSObject <BatchInAppDelegate>
@end
@implementation InAppMsgDelegate
- (void)batchInAppMessageReady:(nonnull BatchInAppMessage*)message
{
// Sample implementation that prevents messages from being displayed if the logged in user isn't the right one
// "loggedInUsername" is an hypothetical property that returns the logged in username as a string, if logged in
if ([[UserManager loggedInUsername] isEqualToString:message.customPayload["username"]])
{
NSError *err;
UIViewController *vc = [BatchMessaging loadViewControllerForMessage:message error:&err];
if (vc)
{
[BatchMessaging presentMessagingViewController:vc];
}
else
{
// Handle the error
}
}
}
@end
let inboxFetcher = BatchInbox.fetcher()BatchInboxFetcher* inboxFetcher = [BatchInbox fetcher];$APP_API_KEY = "abcdef";
$USER_ID = "paul";
$INBOX_SECRET = "foobar";
$HASHED_STRING = $APP_API_KEY . $USER_ID; // "abcdefpaul". Note that . is the PHP concatenation operator. In another language, it usually is +
hash_hmac("sha256", $HASHED_STRING, $INBOX_SECRET) // Returns "796f1ab5d119d1b2eab8201e60335b56d1befff40c0f80263d64a169a8fd2e45"let inboxFetcher = BatchInbox.fetcher(forUserIdentifier: "paul", authenticationKey: "796f1ab5d119d1b2eab8201e60335b56d1befff40c0f80263d64a169a8fd2e45")BatchInboxFetcher* inboxFetcher = [BatchInbox fetcherForUserIdentifier:@"paul" authenticationKey:@"796f1ab5d119d1b2eab8201e60335b56d1befff40c0f80263d64a169a8fd2e45"];func notificationClicked(notification: BatchInboxNotificationContent) {
if (notification.hasLandingMessage) {
notification.displayLandingMessage()
}
}- (void)notificationClicked:(BatchInboxNotificationContent *)notification {
if (notification.hasLandingMessage) {
[notification displayLandingMessage];
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[BatchPush disableAutomaticIntegration];
[BatchSDK startWithAPIKey:@"KEY"];
// You MUST have a UNUserNotificationCenterDelegate implementation.
// Batch provides BatchUNUserNotificationCenterDelegate as a default one: if you have your own, integrate Batch into it.
// See "Intercepting notifications" for more info.
// Available from Batch 1.16
[BatchUNUserNotificationCenterDelegate registerAsDelegate];
// Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
[BatchPush requestNotificationAuthorization];
// Alternatively, you can call requestNotificationAuthorization later
// But, you should always refresh your token on each application start
// This will make sure that even if your user's token changes, you still get notifications
// [BatchPush refreshToken];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[BatchPush handleDeviceToken:deviceToken];
}func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
BatchPush.handleRegister(notificationSettings)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
BatchPush.handleNotification(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
BatchPush.handleNotification(userInfo)
completionHandler(.newData) // Adjust the result accordingly
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[BatchPush handleRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[BatchPush handleNotification:userInfo];
}
// Alternatively, implement this one:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[BatchPush handleNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}BatchSDK.updateAutomaticDataCollection { config in
// Enable GeoIP resolution on server side
config.setGeoIPEnabled(true)
// Enable automatic collection of the device model information
config.setDeviceModelEnabled(true)
}[BatchSDK updateAutomaticDataCollection:^(BatchDataCollectionConfig * _Nonnull config) {
// Enable GeoIP resolution on server side
[config setGeoIPEnabled:true];
// Enable automatic collection of the device model information
[config setDeviceModelEnabled:true];
}];Then, you need to add a few lines to your app delegate in order to receive push notifications.
To ask for the permission to display notifications and register the current device for push you can use one of the following APIs:
1. Obtaining your device token
You can find your device's token using the debug tool or locating the token Batch posts to the Xcode console:
Based on your Provisioning Profile, the token shown in the console will be Development ("Sandbox/Development") or Production ("Production").
If you don't see a push token, there might be an error in the logs describing what happened. See our troubleshooting documentation for more info.
2. Obtaining your Installation ID
You can then retrieve the Installation ID, which represents an installation of your app on a device, by calling the following methods:
While Batch prints this in the debug console on start, displaying it in a settings or about page enables users to send you this identifier. This is useful for debugging, sending test notifications, etc.
3. Sending a test push notification
Batch enables you to send a test notification to the application installation currently running on your device.
To do so, open the dashboard and go to ⚙ Settings → Debug. Enter your Installation ID, hit Debug and then click on "Send Test Push".
You should receive a notification on your device. If not, or if you can't find your Installation ID, the SDK might not be properly configured.
If you need to send push notifications to your device on a regular basis, then you should add your ID as a test device by clicking the "Save as a test device" button.
Congratulations on finishing the integration of Batch Push!
Here are a couple of extra steps you can take before releasing your app:
Rich notifications: Add support for iOS rich push notifications.
Mobile Landings: Make sure Mobile Landings are set up correctly.
Custom user identifier: Add support for custom user identifiers if you are planning to use the Transactional or the Profile APIs.
Analytics: Add an to automatically track your campaigns in your third-party analytics tool.
Universal Links: domains to Batch to open then inside of your app.
You can add action buttons to your notification by adding a category key to the payload of your push. Here is how it looks:
You can set a custom sound for your notification by adding the sound key to your payload. All you need to do is to add your custom sound to your app bundle before choosing it. Here is how it looks:
iOS (7+) supports sending pushes that will wake up your app in the background. It works like background fetching, except that you control when your app will wake up.
This allows you to fetch new content (news feed, conversation list, etc) so your users won't have to wait for your app to sync when then open it.
First, you'll need to go in Xcode and enable "Remote notifications" as a Background Mode in your app's Capabilities:
Then, add "content-available":1 in your aps object:
Users can disable background refresh for your application, or the system might not deliver it in some cases (like battery saving mode).You shouldn't rely on this to work at all times: always fetch data that you can fetch in a more reliable way when your app starts.
Batch supports background notifications, which allow you to run actions for a limited amount of time without notifying the user.
Please keep in mind that the same restrictions apply as the ones described higher up for Background Refresh.
When calling our Campaign/Transactional APIs, add 'push_type': 'background' to the request's JSON body, and remove the message/messages/media keys.
Silent notifications are not supported from the dashboard yet. Using the legacy way to do silent notifications using only the custom payload will NOT work on iOS 13 and later.
As said earlier for background refresh, we advise against triggering local notifications in response to a silent notification. While they are handy, the delivery rate will suffer heavily due to the unreliableness of background refresh.
Since Batch 2.0 the SDK always display notification while the App is in foreground. You can change this behaviour as following:
If you registered BatchUNUserNotificationCenterDelegate as your delegate, use the showForegroundNotifications property.
You can also do so with your own UNUserNotificationCenterDelegate:
Create a class that implements UNUserNotificationCenterDelegate
Override userNotificationCenter(center:willPresentNotification:withCompletionHandler) with willShowSystemForegroundAlert to false and call completionHandler([]).
Call Batch's appropriate methods so you don't break any features
Set it as the default UNUserNotificationCenter's delegate
iOS allows your app to show a button to configure notifications when the user long presses a notification.
You can enable this by calling this API:
Note that this still requires you to implement a UNUserNotificationCenterDelegate, and the appropriate method to open the settings.
In addition of setting a custom user ID, an email address or overriding the language/region, you can assign attributes to your users, allowing you to improve your orchestrations targeting.
Important:
User IDs must be managed using our custom user ID implementation.
Email address must be managed using our email subscription implementation.
Region/language data must be managed using our custom region/language implementation.
Never use an existing tagging plan.
Newly tracked attributes are hidden by default. You will need to manually display them from the dashboard settings.
Before we get started on how to implement attributes, here are some rules you should know.
Naming
Attribute names are strings. They should be made of letters, numbers or underscores ([a-z0-9_]) and can't be longer than 30 characters (e.g. has_premium).
Values
Values must be any of the following types, or their native Swift equivalent:
NSString Must not be longer than 300 characters and cannot be empty. For better results, you should make them upper/lowercase and trim the whitespaces.
NSNumber
Anything bigger than a long long or a double will be rejected.
Unsigned values will be rejected.
Booleans are supported, but should be initialized with [NSNumber numberWithBool:<your value>] or @YES/@NO.
NSDate Since timezones are not supported, this will typically represent UTC dates.
Using any unsupported type as a value (NSNull, NSObject, NSArray, NSDictionary for example) will NOT work. Be careful, as it may cause
[editor save]to fail.
NSURL
Must not be longer than 2048 characters and must follow the format scheme://[authority][path][?query][#fragment].
NSArray<NSString> Must not be longer than 25 items, only values of type NSString and must respect the string attribute limitations.
Setting an attribute
The custom attribute API works using an editor. You need to get an instance of the editor, enqueue your changes and then call save.
Changes will NOT be sent until you call this method, please make sure you call it!
There is one setAttribute/set(attribute:forKey:) method per attribute type. Use Xcode's autocompletion or see BatchProfileEditor.h for all available variants. If you're using Swift, the appropriate method will automatically be used according to the value's type.
Those methods throw an error if key/value failed validation according to the rules expressed higher up in this documentation. Note that no error does not mean the value has already been sent to the server.
You might be tempted to write helpers or loops that open and save many transactions in a row, with each transaction only doing one operation. Doing so prevents Batch from optimizing disk usage and network roundtrips, which impact your user's data plan and battery life. Please try to batch as many operations as you can in a single transaction.
Removing attributes
Use removeAttributeForKey to remove an attribute of any type.
Since Batch SDK v2, updating the user's data also update the profile's data to be accessible from your project scope. This mean the following APIs only read local data related to your installation and NOT to your profile.
You may also have noticed that APIs to set Tags or Tag Collections have been removed and replaced by array attributes. These methods are backward-compatible and array attributes are converted into tag collections to not break your implementation.
Clearing the installation data will delete all tags and attributes set on an installation and their local cache returned by fetchAttributes and fetchTagCollections. It can be done as following:
iOS 10 introduced support for rich notifications: they can now contain custom content, such as images, videos, sounds or even a fully custom view controller. Batch comes with built-in support for these, but due to the way they're implemented, integration of a supplementary SDK is required. Don't worry, we've made it really easy.
In order to set up the Batch Extension SDK, you'll need a notification service extension. It's a standard iOS component that will take care of downloading rich content and add it to the notification.
Open your Xcode project, click on the File menu and then pick New -> Target. Then, pick Notification Service Extension and fill in what's left of the wizard. You can name the extension as you wish: we will name it RichNotificationsExtension for the rest of this tutorial, and write it in Swift.
Xcode will then ask you if you want to activate the scheme. Press Activate.
Before going any further, you might want to check the extension's Deployment Traget. It usually is the latest iOS minor, meaning that your extension will not run on older iOS versions. We recommend that you set it to the lowest version of iOS that your app supports, but not lower than iOS 10.0 as this is the version that introduced this extension kind.
BatchExtension is distributed as an .
Add it using Xcode with its repository URL:
Make sure you're setting the dependency on the extension target. If your Podfile existed before you created your extension, you might need to add it.
If you don't have your own code, you've probably noticed that Xcode added some sample code for you:
In order to have Batch automatically adding rich content to your notifications, simply remplace this code with:
That's it, no code to write! Start your app, and try sending a rich push from the dashboard.
If you've already added your own extension code, you might want to manually integrate Batch and perform your own modifications to the notification content.
First, import the extension SDK:
Then, instanciate a BAERichNotificationHelper/RichNotificationHelper instance, and keep it as an instance variable of your UNNotificationServiceExtension instance.
Note: You must NOT instanciate a new BAERichNotificationHelper/RichNotificationHelper object every time. The class needs to keep an internal state, and might not behave properly if it cannot.
You can then use the following methods of BAERichNotificationHelper/RichNotificationHelper:
didReceive, which has the same signature as the one you're already in, but allows you to tweak the UNNotificationRequest beforehand
appendRichData, which will download and add attachments to the content, and call you back once done.
Here's an example of a class that uses appendRichData:
Starting with version 3.0.0, BatchExtension doesn't download rich notification content in low data mode anymore.
To change this, use BAERichNotificationHelper/RichNotificationHelper:
Batch Actions is a module allowing you to register remotely-configurable runnable code to the SDK, when simple deeplinks wouldn't be enough. They can be triggered at any time by the SDK, allowing you to focus on the action code rather than when to trigger it.
Batch comes with builtin actions (deeplinking, user data edition, etc...)
An action has two components:
An identifier (case-unsensitive string), which will allow you to reference this action
While the identifier string is up to you, it cannot start with "batch.": these identifiers are reserved for built-in actions.
An implementation block
Registering them is easy, and should be done every time your app starts, right before starting Batch:
The source argument represents what caused the action to be triggered. You can use isKindOfClass (or a swift cast using as?) to check what source it comes from, and read what you need.
Two sources classes are currently used: BatchPushMessage and BatchManualUserActionSource. More will be added in the future.
Simply call the unregister method with the previously set identifier:
You might want to re-use the code you've already registered to Batch from your own code by triggering an action.
Simply give Batch the action identifier and arguments, and it will call your action:
Batch provide a set of pre-defined actions, allowing you to easily trigger a specific behavior without implementing them yourself.
batch.dismissSimply dismiss the notification and exit
Arguments
N/A
batch.deeplinkOpen the provided deeplink (will call the Deeplink Interceptor if one is set).
Arguments (required)
batch.ios_request_notificationsShow the notification permissions pop-up.
Arguments
N/A
batch.ios_redirect_settingsOpen the notifications settings of the current application.
Arguments
N/A
batch.ios_smart_reoptinChecks if the user has already been asked for notifications, if not it shows the notification permissions pop-up, otherwise it opens the notifications settings of the current application.
Arguments
N/A
batch.user.tagAdd or remove a tag associated with the current user.
Arguments (required)
batch.user.eventSend an event with associated tags and data.
Arguments (required)
batch.groupExecute a list of up to 10 nested actions successively.
Arguments (required)
batch.ios_tracking_consentOn iOS 14, asks for Tracking consent using the AppTrackingTransparency framework. If the user has already been asked and refused to consent, opens the app's settings where tracking can be enabled. Does nothing on iOS 13 and earlier, or if tracking has been restricted by the device's manager. Your application needs to have a 'NSUserTrackingUsageDescription' key in its Info.plist or else iOS will crash it. Available since Batch 1.16.
Arguments
N/A
batch.clipboardCopy a text to the device's clipboard.
Arguments (required)
batch.ratingOn iOS 10.3+, asks for the user to rate the app using SKStoreReviewController (this API's restrictions apply). Available since Batch 1.17.
Arguments
N/A
Batch allows you to track events that happen in your application. They automatically keep track of their count, the last time it happened and their value.
Important Newly tracked events are hidden by default. You will need to manually display them from the dashboard settings.
Events are easy to use, but have some rules:
import Batch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Start Batch.
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
// Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
BatchPush.requestNotificationAuthorization()
// Alternatively, you can call requestNotificationAuthorization later
// But, you should always refresh your token on each application start
// This will make sure that even if your user's token changes, you still get notifications
// BatchPush.refreshToken();
// Sets Batch as your UNUserNotificationCenterDelegate.
// This will disable the legacy callbacks on your app delegate (didReceiveRemoteNotification, ...).
// If you rely on those, do not add this line and please consider migrating to the UserNotification framework.
//
// If you already have a UNUserNotificationCenterDelegate implementation, do not add this line.
// Instead, add Batch's callbacks in your implementation. See 'Advanced > Intercepting notifications',
BatchUNUserNotificationCenterDelegate.registerAsDelegate()
return true
}@import Batch;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Start Batch.
[BatchSDK startWithAPIKey:@"YOUR_API_KEY"];
// Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
[BatchPush requestNotificationAuthorization];
// Alternatively, you can call requestNotificationAuthorization later
// But, you should always refresh your token on each application start
// This will make sure that even if your user's token changes, you still get notifications
// [BatchPush refreshToken];
// Sets Batch as your UNUserNotificationCenterDelegate.
// This will disable the legacy callbacks on your app delegate (didReceiveRemoteNotification, ...).
// If you rely on those, do not add this line and please consider migrating to the UserNotification framework.
//
// If you already have a UNUserNotificationCenterDelegate implementation, do not add this line.
// Instead, add Batch's callbacks in your implementation. See 'Advanced > Intercepting notifications',
[BatchUNUserNotificationCenterDelegate registerAsDelegate];
return YES;
} // Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
BatchPush.requestNotificationAuthorization()
// Ask for the permission to display notifications with a completion handler.
// The push token will automatically be fetched by the SDK
BatchPush.requestNotificationAuthorization { success, error in
// Handle permission result
}
// Ask for the provisionnal permission to display notifications
// Notifications will NOT be displayed on the lock screen, or as a banner when the phone is unlocked.
// They will directly be sent to the notification center, accessible when the user swipes up on the lockscreen, or down
// The push token will automatically be fetched by the SDK.
BatchPush.requestProvisionalNotificationAuthorization() // Ask for the permission to display notifications
// The push token will automatically be fetched by the SDK
[BatchPush requestNotificationAuthorization];
// Ask for the permission to display notifications with a completion handler.
// The push token will automatically be fetched by the SDK
[BatchPush requestNotificationAuthorizationWithCompletionHandler:^(BOOL granted, NSError * _Nullable error) {
// Handle permission result
}];
// Ask for the provisionnal permission to display notifications
// Notifications will NOT be displayed on the lock screen, or as a banner when the phone is unlocked.
// They will directly be sent to the notification center, accessible when the user swipes up on the lockscreen, or down
// The push token will automatically be fetched by the SDK.
[BatchPush requestProvisionalNotificationAuthorization];
}BatchUser.installationID()[BatchUser installationID];[Batch] - Push token (Apple Push Production): <push token>// If you did not use BatchUNUserNotificationCenterDelegateregisterAsDelegate() but instanciated your own instance, use it instead of `sharedInstance`.
BatchUNUserNotificationCenterDelegate.sharedInstance.showForegroundNotifications = false// If you did not use [BatchUNUserNotificationCenterDelegate registerAsDelegate] but instanciated your own instance, use it instead of `sharedInstance`.
[BatchUNUserNotificationCenterDelegate sharedInstance].showForegroundNotifications = false;{"aps":{"badge":2}}{"aps":{"category":"CATEGORY_NAME"}}{"aps":{"sound":"mysound.caf"}}{"aps":{"content-available":1}} [BatchPush setSupportsAppNotificationSettings:true]






l
String - Required
The deeplink to open
E.g.{"l":"https://google.com"}
li
Boolean - Optional - Default false
If true the Batch SDK will try to open your deeplink inside the app
E.g.{"li":false}
a
String - Required
The action to perform.
Acceptable values are add and remove
E.g.{"a":"remove"}
c
String - Required
The collection to use when updating the tag
E.g.{"c":"actions"}
t
String - Required
The tag to update
E.g.{"t":"has_bought"}
e
String - Required
The name of the event to send
E.g.{"e":"my_awesome_event"}
l
String - Optional - default null
The label associated with the event
E.g.{"l":"label1"}
t
Array - Optional - default empty list
A list of tags to be send with the event
E.g.{"t":["tag1","tag2","tag3"]}
a
Object - Optional - default empty object
The data to send with the event
E.g.{"a":{"key":"value","key2":12,"key3":true}}
actions
Array - Required
A list of actions to perform. This list must not be longer than 10 elements and can't contains batch.group actions
E.g.{"actions":[["batch.deeplink",{"l":"https://google.com"}],["batch.user.tag",{"add":"..."}]]}
t
String - Required
The text to copy
E.g.{"t":"My awesome text !"}
BatchActions.register(BatchUserAction(identifier: "<YOUR_ACTION_NAME>",
actionBlock: { (identifier: String, arguments: [String : Any], source: BatchUserActionSource?) in
// Your action code here
}))BatchUserActionBlock alertActionBlock = ^(NSString * _Nonnull identifier, NSDictionary<NSString *,NSObject *> * _Nonnull arguments, id<BatchUserActionSource> _Nullable source) {
// Your action code here
};
[BatchActions registerAction:[BatchUserAction userActionWithIdentifier:@"<YOUR_ACTION_NAME>"
actionBlock:alertActionBlock]];https://github.com/BatchLabs/Batch-iOS-SDK-Extension.gittarget 'RichNotifications' do
use_frameworks!
pod 'BatchExtension'
end
target 'App' do
use_frameworks!
pod 'Batch'
endgithub "BatchLabs/Batch-iOS-SDK-Extension"import BatchExtension
class NotificationService: BAENotificationServiceExtension {
}// NotificationService.h --
@import BatchExtension;
@interface NotificationService : BAENotificationServiceExtension
@end
// NotificationService.m
#import "NotificationService.h"
@implementation NotificationService
@end
import BatchExtension@import BatchExtension;
or
#import <BatchExtension/BatchExtension.h>class NotificationService: UNNotificationServiceExtension {
let batchHelper = RichNotificationHelper()
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
if let bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
// Ask Batch to download and add any rich content
batchHelper.appendRichData(toContent: bestAttemptContent, completionHandler: { (content: UNNotificationContent?, err: Error?) in
if let err = err {
print("Error while appending rich notification attachments \(err)")
}
contentHandler(content ?? bestAttemptContent)
})
} else {
contentHandler(request.content)
}
}
}// NotificationService.h
#import <UserNotifications/UserNotifications.h>
@interface NotificationService : UNNotificationServiceExtension
@end
// NotificationService.m
#import "NotificationService.h"
@import BatchExtension;
@interface NotificationService () {
BAERichNotificationHelper *batchHelper;
}
@end
@implementation NotificationService
- (instancetype)init {
self = [super init];
if (self) {
batchHelper = [BAERichNotificationHelper new];
}
return self;
}
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
UNMutableNotificationContent *bestAttemptContent = [request.content mutableCopy];
if (bestAttemptContent) {
// Modify the notification content here...
bestAttemptContent.title = [bestAttemptContent.title stringByAppendingString:@" [modified]"];
// Ask Batch to download and add any rich content
[batchHelper appendRichDataToContent:bestAttemptContent completionHandler:^(UNNotificationContent * _Nullable result, NSError * _Nullable error) {
if (error) {
NSLog(@"Error while appending rich notification attachments %@", error);
}
if (result) {
contentHandler(result);
} else {
contentHandler(bestAttemptContent);
}
}];
} else {
contentHandler(bestAttemptContent);
}
}
@end
import UserNotifications
import BatchExtension
class NotificationService: UNNotificationServiceExtension {
override init() {
super.init()
RichNotificationHelper.allowInLowDataMode = true
}
}// NotificationService.m
#import "NotificationService.h"
@implementation NotificationService
- (instancetype)init
{
self = [super init];
if (self) {
BAERichNotificationHelper.allowInLowDataMode = true;
}
return self;
}
@end
BatchActions.unregister(actionIdentifier: "<YOUR_ACTION_NAME>")[BatchActions unregisterActionIdentifier:@"<YOUR_ACTION_NAME>"];BatchActions.perform(actionIdentifier: "alert", arguments: ["title": "Hello", "body": "How are you doing?"])[BatchActions performActionIdentifiedBy:@"alert" withArguments:@{@"title":@"Hello", @"body":@"How are you doing?"}];A custom data object can be attached. See the section "Event Attributes", right under this one.
Custom attributes have some reserved keys. See the section "Reserved event attributes" under "Event Attributes" for more info.
Here are some examples:
// Simple event
BatchProfile.trackEvent(name: "ad_seen")
// Event with custom attributes
BatchProfile.trackEvent(name: "add_to_cart", attributes: BatchEventAttributes { data in
// Custom attribute
data.put("bags"
// Simple event
[BatchProfile trackEvent:@"ad_seen"];
// Event with custom attributes
BatchEventAttributes *attributes = [BatchEventAttributes new];
[attributes putString:@"sub_category" forKey:@"bags"]; // Custom attributes
[attributes putString:
Please test your implementation using our debug tool and profile view before releasing your app on the store.
Custom attributes can be attached to events using BatchEventAttributes. You will then use them when calling BatchProfile.trackEvent().
Attribute name
They should be made of letters, numbers or underscores ([a-z0-9_]) and can't be longer than 30 characters (e.g. has_premium). They will be automatically lowercased, so trying to use the same key with different casing will overwrite the previously set value.
Attribute value
Values must be any of the following types:
NSString, must not be longer than 300 characters and can't be empty. For better results, you should make them upper/lowercase and trim the whitespaces.
Bool
Floats/Doubles
NSInteger
NSDate
NSURL, not longer than 2048 characters and must follow the format scheme://[authority][path][?query][#fragment].
NSArray<NSString | BatchEventAttributes>, You can set array of strings (max 300chars) and array of objects. Max 25 items. You cannot mix several attribute types in one array. Arrays can't be nested.
Object (using BatchEventAttributes, Objects cannot have more than 3 levels of nesting.
Reserved event attributes
Some event attributes have reserved keys, and are all prefixed by a $ sign. This is the list of currently reserved event attributes. You cannot set an event attribute starting by a $ sign.
$label
String - Optional
Event label. Must be a string, will automatically be bridged as label for application event compatibility. Max 200 chars.
$tags
NSArray - Optional
Event tags. Must be an array of string, will automatically be bridged as tags for application event compatibility. Max 10 items of type string, each no longer than 64chars. The SDK will automatically lowercase them, so two same strings with different casing do not count as two different tags
In Batch SDK v1 you were able to set a label and tags at the root of an event, with the limit of 1 label and 10 tags.
Batch SDK v2 introduced Object and Array types in event attributes. You can set more than one array on profiles events. This is only supported for profiles, and not on the install-centric data model, which currently powers push notification and In-App messages.
However, it's still possible to set a label and tags on events in the install-centric data model by using $label and $tags, and activate compatibility flows. This way, you will be able to use this data for your push and in-app communications.
If you are not running any orchestrations on the Mobile Engagement Platform (MEP), you should use String and Array attributes instead of legacy $label and $tags.
Example
You can now natively track a user location. This uses CoreLocation's standard CLLocation object, which you usually get from the CoreLocation itself. You can also instantiate one manually from a latitude/longitude.
Here's an example:
This data will allow you to send geo-targeted push notifications from the dashboard or the Campaigns API.
Events can be sent while the application is in the background by asking UIApplication to begin a background task. Once the event has been sent to the server, Batch will emit a BatchEventTrackerFinishedNotification NSNotification.
Please note that this notification might be sent multiple times: you may want to dynamically add the observer and remove it once your event has been tracked.
Here is a sample implementation:

If you want to see a proper integration of the Batch SDK and to test any of its functionalities, you can take a look at our sample apps.
→ Download the sample app on GitHub
They are currently written for iOS in both Swift and Objective-C.
Step 1
First, download the SDK and Include Batch.xcframework in your project by dragging and dropping it into the Project Navigator as shown here:
Unless you manually copied the XCframework folder into your project's directory, check Copy items if needed. Make sure the only checked target is your Application target: extensions should be unchecked.
Batch servers need to have a certificate in order to communicate with Apple Push Notification Services (APNS). This certificate is generated for an unique Application Identifier (App ID).
Step 1.
Open your Keychain application on your Mac. In the menu bar, select "Keychain Access" → "Certificate Assistant" → "Request a Certificate From a Certificate Authority…"
Then, enter your information in the required fields and select "Saved to disk". Save the "CertificateSigningRequest.certSigningRequest" file to your Desktop.
Now you have the certificate, you need to have it with its key bundled into a .p12 file format.
Step 1.
First, add the certificate to your Keychain by double clicking on the downloaded SSL certificate.
In Keychain Access, pick the default keychain (login on English systems) using the sidebar. Then, click on "My Certificates" and find the certificate you just added. It should be called "Apple Push Services:".
After creating a new App ID or modifying your existing one, you need to (re)generate your provisioning profile and build with it.
Step 1.
Go to , then click on "Provisioning Profiles" from the iOS Apps section.
Click on the "+" button to create a new iOS Provisioning Profile or select an existing one.
If Batch tells you that it was unable to automatically integrate with your application delegate, it's probably because there is a conflict with another SDK.
Try calling [BatchSDK startWithAPIKey:] earlier in your application delegate.
let attributes = BatchEventAttributes { data in
data.put("man_clothes", forKey: "sub_category")
data.put(Date(timeIntervalSince1970: 1713432899086), forKey: "end_of_sale_date")
data.put([
BatchEventAttributes { itemData in
itemData.put("Basic Tee", forKey: "name")
itemData.put("M", forKey: "size")
itemData.put(23.99, forKey: "price")
itemData.put(URL(string: "https://batch-store.com/basic-tee")!, forKey: "item_url")
itemData.put(URL(string: "https://batch-store.com/basic-tee/black/image.png")!, forKey: "item_image")
itemData.put(true, forKey: "in_sales")
},
BatchEventAttributes { itemData in
itemData.put("Short socks pack x3", forKey: "name")
itemData.put("38-40", forKey: "size")
itemData.put(15.99, forKey: "price")
itemData.put(URL(string: "https://batch-store.com/short-socks-pack-x3")!, forKey: "item_url")
itemData.put(URL(string: "https://batch-store.com/short-socks-pack-x3/image.png")!, forKey: "item_image")
itemData.put(false, forKey: "in_sales")
}
], forKey: "items_list")
data.put("accessories", forKey: "$label")
data.put(["first_purchase", "in_promo"], forKey: "$tags")
}
do {
let _ = try attributes.validate()
BatchProfile.trackEvent(name: "purchased", attributes: attributes)
} catch let error {
print("Event validation error: \(error.localizedDescription)")
}BatchEventAttributes *attributes = [BatchEventAttributes new];
[attributes putString:@"man_clothes" forKey:@"sub_category"];
[attributes putDate:[NSDate dateWithTimeIntervalSince1970:1713432899086] forKey:@"end_of_sale_date"];
NSMutableArray *itemsList = [NSMutableArray array];
BatchEventAttributes *item1 = [BatchEventAttributes new];
[item1 putString:@"Basic Tee" forKey:@"name"];
[item1 putString:@"M" forKey:@"size"];
[item1 putFloat:23.99 forKey:@"price"];
[item1 putURL:[NSURL URLWithString:@"https://batch-store.com/basic-tee"] forKey:@"item_url"];
[item1 putURL:[NSURL URLWithString:@"https://batch-store.com/basic-tee/black/image.png"] forKey:@"item_image"];
[item1 putBool:YES forKey:@"in_sales"];
[itemsList addObject:item1];
BatchEventAttributes *item2 = [BatchEventAttributes new];
[item2 putString:@"Short socks pack x3" forKey:@"name"];
[item2 putString:@"38-40" forKey:@"size"];
[item2 putFloat:15.99 forKey:@"price"];
[item2 putURL:[NSURL URLWithString:@"https://batch-store.com/short-socks-pack-x3"] forKey:@"item_url"];
[item2 putURL:[NSURL URLWithString:@"https://batch-store.com/short-socks-pack-x3/image.png"] forKey:@"item_image"];
[item2 putBool:NO forKey:@"in_sales"];
[itemsList addObject:item2];
[attributes putObjectArray:itemsList forKey:@"items_list"];
[attributes putString:@"accessories" forKey:@"$label"];
[attributes putStringArray:@[@"first_purchase", @"in_promo"] forKey:@"$tags"];
NSError *err;
[attributes validateWithError:&err];
if (err == nil) {
[BatchProfile trackEventWithName:@"purchased" attributes:attributes];
} else {
NSLog(@"Event validation error: %@", err.description);
}// let location: CLLocation = [...]
BatchProfile.trackLocation(location)// CLLocation *location = [...];
[BatchProfile trackLocation:location];@objc
class BackgroundEventSender : NSObject {
var eventBackgroundEventTaskID: UIBackgroundTaskIdentifier?
override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(finishBackgroundTask), name: Notification.Name.BatchEventTrackerFinished, object: nil)
}
func trackBackgroundEvent() {
guard eventBackgroundEventTaskID == nil else {
// This sample code doesn't support tracking multiple events at once
return
}
eventBackgroundEventTaskID = UIApplication.shared.beginBackgroundTask(withName: "batch_background_event", expirationHandler: self.finishBackgroundTask)
BatchProfile.trackEvent(name: "SAMPLE_BACKGROUND_EVENT")
}
@objc
func finishBackgroundTask() {
guard let task = eventBackgroundEventTaskID else {
return
}
self.eventBackgroundEventTaskID = nil
UIApplication.shared.endBackgroundTask(task)
}
}// Get an editor instance.
// You need to save this in a local variable until you call save
// Editor instances don't share changes, and calling save on an empty editor will do nothing
let editor = BatchProfile.editor()
// Set an attribute. try? allows a potential error to be silently ignored
// This example is a valid key/attribute pair, and will not throw an error.
try? editor.set(attribute: 26, forKey:"age")
// Set an array attribute
try? editor.set(attribute: ["add_to_cart", "has_bought"], forKey: "actions")
// Add a string to an array attribute
try? editor.addToStringArray(item: "has_bought", forKey: "actions")
// Set an attribute with error handling.
do {
// Invalid attribute name, $ is a forbidden character
try editor.set(attribute: "patricia", forKey: "fir$t_name")
} catch {
// Handle the error here.
// Error is of type BatchProfileError if you want to specifically
// handle it.
}
// Don't forget to save the changes
editor.save()
// Alternatively you can use `editWithBlock` and make all your changes in a closure.
BatchProfile.editor { editor in
try? editor.set(attribute: "patricia", forKey: "firt_name")
// No need to save here since Batch will automatically save your change after executing the block.
}// Get an editor instance.
// You need to save this in a local variable until you call save
// Editor instances don't share changes, and calling save on an empty editor will do nothing
BatchProfileEditor *editor = [BatchProfile editor];
// Set an attribute, silently ignoring a potential error
// This example is a valid key/attribute pair, and will not throw an error.
[editor setIntegerAttribute:@26 forKey:@"age" error:nil];
// Set an array attribute
[editor setStringArrayAttribute: @[@"added_to_cart", @"has_bought"] forKey:@"actions" error:nil];
// Add a string to an array attribute
[editor addItemToStringArrayAttribute:@"actions" forKey:@"has_bought" error:nil]
// Set an attribute with error handling.
NSError *err;
if (![editor setStringAttribute:@"patricia" forKey:@"fir$t_name" error:&err]) { ; // Invalid attribute name, $ is a forbidden character
// Handle the error here.
}
// Don't forget to save the changes
[editor save];
// Alternatively you can use `editWithBlock` and make all your changes in a closure.
[BatchProfile editWithBlock:^(BatchProfileEditor * _Nonnull editor) {
[editor setStringAttribute:@"patricia" forKey:@"firt_name" error:nil];
// No need to save here since Batch will automatically save your change after executing the block.
}];// Get an editor instance.
// You need to save this in a local variable until you call save
// Editor instances don't share changes, and calling save on an empty editor will do nothing
let editor = BatchProfile.editor()
// Remove an attribute
editor.removeAttribute(forKey: "age")
// Remove a string from an array
try? editor.removeFromStringArray(item: "has_bought", forKey: "actions")
// Don't forget to save the changes
editor.save() // Get an editor instance.
// You need to save this in a local variable until you call save
// Editor instances don't share changes, and calling save on an empty editor will do nothing
BatchProfileEditor *editor = [BatchProfile editor];
// Remove an attribute
[editor removeAttributeForKey:@"age"];
// Remove a string from an array
[editor removeItemFromStringArrayAttribute:@"has_bought" forKey:@"actions" error:nil];
// Don't forget to save the changes
[editor save];BatchUser.fetchAttributes { attributes in
// Attributes are retrieved in the form of a dictionary
// Values are encapsulated in an instance of BatchUserAttribute
let attribute: BatchUserAttribute = attributes["age"]
// BatchUserAttribute holds a reference to the value of the attribute
let rawValue: Any = attribute.value // Raw value is not typed
print(rawValue) // Prints "NSNumber(26)"
// The type of the value is specified via a BatchUserAttributeType enumeration
print(attribute.type) // Prints "BatchUserAttributeTypeLongLong"
// To obtain a typed result you can use one of the four helper methods
attribute.numberValue() // Will return "26" here
attribute.dateValue() // Will return nil here
attribute.stringValue() // Will return nil here
attribute.urlValue() // Will return nil here
}[BatchUser fetchAttributes:^(NSDictionary<NSString *,BatchUserAttribute *> * _Nullable attributes) {
// Attributes are retrieved in the form of a dictionary
// Values are encapsulated in an instance of BatchUserAttribute
BatchUserAttribute *attribute = attributes[@"age"];
// BatchUserAttribute holds a reference to the value of the attribute
id rawValue = attribute.value; // Raw value is not typed
NSLog(rawValue); // Prints "NSNumber(26)"
// The type of the value is specified via a BatchUserAttributeType enumeration
NSLog(attribute.type); // Prints "BatchUserAttributeTypeLongLong"
// To obtain a typed result you can use one of the three helper methods
[attribute numberValue]; // Will return "26" here
[attribute dateValue]; // Will return nil here
[attribute stringValue]; // Will return nil here
}];BatchUser.fetchTags { tagCollections in
// Tags are also retrieved in the form of a dictionary
// Keys are names of collections, values are sets of tags
let tagCollection: Set<String> = tagCollections["actions"]
print(tagCollection) // Prints "["has_bought"]"
}[BatchUser fetchTags:^(NSDictionary<NSString *,NSSet<NSString *> *> * _Nullable tagCollections) {
// Tags are also retrieved in the form of a dictionary
// Keys are names of collections, values are sets of tags
NSSet<NSString *> *tagCollection = tagCollections[@"actions"];
NSLog(tagCollection) // Prints "["has_bought"]"
}];BatchUser.clearInstallationData();[BatchUser clearInstallationData];Be sure Batch.xcframework and libsqlite3.tbd are in the Build Phases of your application target:
Step 3
In order to avoid a link issue when building your application, add -ObjC -lz to Other Linker Flags, under Build Settings of your application target.
Head to the Apple Developer Member center, open the Identifiers section and create a new App ID (or choose an existing Explicit App ID) to configure Push Notifications.
Scroll down to the Push Notifications section, check it if needed and click configure.
Under "Production SSL Certificate", click on "Create Certificate...". The Production certificate can push both sandbox and production environments.
The next screen will ask us to upload the Certificate Signing Request (CSR), which we have created earlier.
Select "Choose File…" and locate the .certSigningRequest file on your desktop. Finally, select "Continue" and download the generated SSL certificate.
Step 2.
Make sure that the certificate is collapsed (the arrow on the left ( > ) should point to the right). Right-click on the certificate, select "Export Apple Push Services:…", and save it as a .p12 file.
You will be prompted to enter a password which will be used to protect the exported certificate.
Step 3.
Now go to Batch's dashboard → ⚙ Settings → Channel -> iOS, and upload your P12 certificate.
Step 2.
In the Provisioning Profile creation wizard:
Select Type: Choose "App Store".
Configure: Choose the App ID you created before from the drop down and make sure to select your iOS Production certificate.
Generate: Choose a name for this provisioning profile, such as "Batch Push App Profile" and select "Generate"
Then download the generated provisioning profile from the next screen by selecting the "Download" button.
Step 3.
Install the profile by double-clicking on the downloaded file.
Your new provisioning profile should appear in the Provisioning Profiles section in your Xcode settings under the "Accounts" section. Make sure that your developer certificate is installed in your Keychain.




If you are having trouble integrating the SDK, uploading your certificates or sending notifications, here are some suggestions.
The Installation ID is an ID generated by Batch for all the installs the first time your users open the app. You can safely display it in your app to simplify the debug process.
You can use that ID in the debug tool (Dashboard settings > Debug) to find your own install, see the data Batch has on it and send test notifications to your device.
Your end users can also send it to you so you can understand why they are not receiving push notifications or not seeing an In-App message. This is useful when your app doesn't share any advertising ID with Batch.
You can also store the install ID on your end and reuse it later to target specific installs using the .
You can retrieve the installation ID by calling the following methods:
That installation ID can be exposed to your end users. You can insert it in a diagnostic email automatically generated when users report an issue from the app or hide it in an easter egg.
Batch will copy the installation ID to the clipboard when the application appears in the foreground 4 times within 20 seconds. This is enabled by default, you can disable it at any time by using:
Internal logs can be useful to you or our support team to debug issues that might not be debuggable using public logs. They're disabled by default as they are very verbose by design.
They can be enabled in two ways:
Please make sure that you do not enable internal logs on an App Store build.
Command line arguments are only usable when an app is launched by Xcode or while running tests. This is an easy way to make sure that logs never end up in a production build. However, they cannot be allow for internal logs to be read by testers.
Batch will enable internal logs if the app has been launched with -BatchSDKEnableInternalLogs:
The debug view is a UI with multiple debug features, allowing you to see the native and custom data attached to your install. You can also see the list of In-App campaigns already stored in your device.
This is useful for development purposes and for internal uses only, especially if your team cannot use an advertising or a custom user id to find their device in the debug tool and send test notifications to their device.
Shows a list of native information you can use for basic debug:
Batch SDK version
Installation ID
Advertising ID
Push token
You can easily share that report by clicking the share button in the top right corner.
Shows the custom user data attached to your install, excluding events. This is useful to:
Check the Custom User ID attached to the install ().
See the list of attributes / tag collections attached to your install. You can use that part to see if the app has been tagged correctly or understand why your device didn't receive a notification based on a one of these information.
Use that part to manually pull the campaigns available on Batch servers for your install and to see the list of stored In-App campaigns.
The debugger should be presented modally:
You can hide it in an easter egg (e.g. display it after 10 taps on specific button, etc) or simply display it in your app settings for your development versions exclusively.
There are several points you should check if Batch doesn't show any data on your dashboard:
24h delay. Installs, DAU, starts and redeems shown on the Analytics tab are updated on a 24h basis.
Integration issue. If you still don't see any stats for your app 24h after changing the API key, see if you find anything related to Batch in your logs and double check your integration using the documentation.
There are several points you should check if Batch doesn't detect any device tokens:
Push disabled: See if you have enabled the notifications for your app in the iOS settings (Settings → Notifications).
Look for errors: Check your Xcode logs to see if Batch published any error messages.
Provisioning issue: Make sure you have correctly set up your provisioning profile with Xcode.
If you are not receiving any of the notifications sent from Batch, here are some suggestions to find the issue:
App opened: Make sure your app is not opened in the foreground during your tests. By default iOS only displays notifications for apps opened in the background: It only calls your delegate, which can be invisible to you if you don't log anything.
Push disabled: See if you have enabled the notifications for your app in iOS settings (Settings → Notifications).
Connection issues: Connect your device to a 3G/4G network or try to disconnect/reconnect to your WiFi network.
You may receive the same notification twice if you have reinstalled your app with different API keys, several times on the same device (Dev/Live). Rest assured, your users won't receive the same notification twice.
Make sure you have selected the default login keychain in the left pane of the Keychain application, and selected "My certificates" in the top filtering bar of the Keychain application.
First, double-check your certificate password. If your password is correct, make sure your certificate is not named "Apple Production IOS Push Services" or "Apple Development IOS Push Services". These are not supported anymore. You will need to if it's the case.
You cannot upload Apple's certificate directly. You will find more information on certificate generation .
Development certificates or production certificates named "Apple Production IOS Push Services" are not supported anymore. Please generate a new .
DeviceTokenNotForTopic errors happen when your app's bundle ID and certificate's bundle ID don't match.
Please ensure you set up your bundle ID correctly in your Xcode project settings.
BadDeviceToken errors can happen for two reasons:
Invalid token: On iOS 9 and higher, your device token may change if you uninstall/reinstall an app. Please make sure your token hasn't changed since your last tests. See how to obtain your token .
Wrong environment: If your device token is valid, make sure you have chosen the right environment (sandbox/production) from Batch settings (Settings → Push settings). You can find the right environment for your token with our .
If you see an "Invalid certificate" error, this means that:
Legacy certificates: You are currently using a legacy certificate and the Development or the Production certificates are missing.
Delay: Batch is still processing your certificates. Please try again in a few minutes and reach us if the problem persists.
If you are getting an unknown error, please contact us at or by clicking on the question mark in the bottom right corner. Our team will try to find more details on the issue.
You can find the list of APNS and Batch internal errors in the , by hovering on the error count.
APNS can send this feedback when:
Invalid token: The token Batch tries to push is not valid anymore. This usually happens when users uninstall/reinstall (iOS 9.x and higer) your app or disable notifications.
Wrong environment: Make sure you are targeting the right environment (sandbox/production) for your token.
Happens when your app's bundle ID and certificate's bundle ID don't match. Make sure you set up your bundle ID correctly in your Xcode project settings.
Make sure the App ID/Bundle ID or the team ID you used for your P8 certificate are correct.
This internal error happens when the number of maximum send attempts is reached for a token. Feel free to reach us if you are seeing to many max_retry_attemps errors.
iOS 8 introduced actionnable notifications: action buttons can be attached to notifications. Batch includes support for them: this documentation describes how to make the best of Batch's features and how to integrate them with iOS' native features, including how to work with the new iOS 10 APIs without breaking backwards compatibility.
This guide has no intention of being a full replacement of Apple's official documentations of notification actions.
In order to be able to use action buttons in your notifications, you will have to register them to the framework in the application itself.
To do so, you'll create Actions and group sets of them into Categories. Categories are what iOS will use to display a set of predefined buttons in local or remote notifications. It is important that you figure out what categories you will want to associate to your notifications as updating them will require you to submit an updated build to the App Store and have your users update your app before you can push with your new actions.
Actions are defined by:
An identifier. Your app will be called back with this identifier once the user tapped on the action so you can perform the action.
A label. It is the text that the user will see.
Optional behavior options such as:
Destructive (the button will appear red)
You can delcare these actions using the standard Apple UIKit documentation / .
Once you've declared your actions and put them into categories, you can register them to Batch:
You can also use the native UIKit APIs to register them, as per Apple's documentation.
iOS 10 (UserNotifications)
If you're using the UserNotifications framework, iOS will call you back in your UNUserNotificationCenterDelegate instance you've set as the UNUserNotificationCenter delegate.
For that, you'll need to implement
You'll also need to make sure you don't accidentally try to run your own code for actions that are not linked to action buttons:
Earlier iOS versions
As most legacy notifications callbacks, iOS will call you back on your app delegate.
You'll need to implement .
A great way to clean up your code is to use and link them to your action buttons. It allows you to share code between the legacy and new callbacks, but also make your actions available to and any future feature that will support calling actions you've defined remotely.
Once you've created your , make sure you register your UNNotificationAction instances with the same identifier as the ones you've picked for your Batch Custom Actions.
Then, you can trigger them manually from the callbacks:
Starting with iOS 10, Apple has deprecated UIUserNotificationAction and related classes in favor of UNNotificationAction. Applications compatible with iOS version lower than iOS 10 will have to deal with compatibility.
There are three ways of dealing with this:
The way is the recommended one. You can use Objective-C/Swift feature detection and register accordingly:
Note: A full example of this implementation can be found in our
If you're using Batch to register for notifications rather than UIKit methods, switching to the new UserNotifications framework has already been done for you.
Calling BatchPush.registerForRemoteNotifications(<Categories set>) with legacy (UI*UserNotification) actions and categories will cause Batch to convert them to UNNotificationAction and UNNotificationCategory instances and use the new APIs transparently.
If you define categories with both Default and Minimal contexts for your action sets, Batch will complain in the logs, as this doesn't exist anymore with the new APIs. A long term fix is to migrate to the right API according to the iOS version you're running on, as described earlier.
Registering notifications this way is supported by iOS, but should not be used: You should use the UserNotifications framework on iOS 10, since the old APIs have been deprecated.
You'll also have better callback support for anything related to notifications in your app.
You can now add actions to your iOS notifications by adding a category key to the payload of your push. Here is how it looks:
Batch SDK v2 is a major release, which introduces breaking changes from 1.x. This guide describes how to update your application when using a previous version.
⚠️ Batch SDK 2.0 now requires Xcode 15.3 and iOS 13.0 or higher.
To upgrade from v1 to v2, you need to change the SDK version according to your package manager:
Update by using the "update package" option from Xcode or updating your package.swift.
.package(url: "https://github.com/BatchLabs/Batch-iOS-SDK", from: "2.0.0")Update your Podfile as following and run pod update.
Update your Cartfile if necessary and run carthage update Batch.
Batch is now distributed as a dynamic framework. This mean you can now safely link Batch between multiple framework targets!
Since Batch is now a , the additional metadata induced by this feature makes the XCFramework distribution heavier, but has no effect in the final size of Batch in your app once compiled.
This version introduced the visionOS support but some features are still unsupported like:
In-App messaging and mobile landings are unavailable.
In-App rating is not supported on visionOS due to an OS limitation.
This version introduced an important change since the default Batch module has been renamed into BatchSDK. This mean you have to replace all your calls to this module like when starting the SDK.
The iOS Batch SDK 1.21 had removed automatic collection of IDFA (Identifier For Advertisers). This version has totally dropped the support of the IDFA and you can no longer set an advertising id to Batch since all related APIs have been removed.
The Batch SDK v1 allowed you to disable advanced information generally with setUseAdvancedDeviceInformation(false). This has been removed and replaced with a more fine-tuning control of what you want to enable with:
For more information, please visit our .
All deprecated APIs in the SDK v1 have been removed. Some other APIs have been renamed/reworked to feel Swift native. To see in details the differences, please visit our .
This version follows Batch's pivot to being an omnichannel platform. It allows you to collect data for your Projects and Profiles. If you are not familiar with these two concepts, please see beforehand.
First of all, most of the user-related write APIs have been removed. Reading methods are still usable since we do not provide yet a way to get synced data for a Profile, but keep in mind that the data returned is only about your installation and not your Profile.
To interacts with our user-centered model, you should now use the BatchProfile module. Let's see a migration example.
If you were previously doing something like that:
You should now do:
For more information, please see the following sections :
To make it easier to collect data to a Profile, Batch has added two automatic ways to migrate old installation's data on a Profile. So the first time a user will launch your application running on v2 :
He will be automatically identified (logged-in) if he had a Batch custom_user_id set on the local storage.
Its natives (language/region) and customs data will be automatically migrate to a Profile if your app is attached to a Project.
These migrations are enabled by default, but you may want to disable them, to do so add the following before starting the SDK:
For more information, please visit our
Android Batch SDK v2 introduced two new types of attribute that can be attached to an event : Array and Object.
What's change:
BatchEventData has be renamed into BatchEventAttributes.
addTag API is no longer available and has been replaced by putStringArray with the $tags reserved key.
Optional withLabel
Note: Limits are unchanged and still 200 chars max for $label and 10 items max for $tags .
So if you were previously doing something like:
You should now do:
For more information, please visit our .
To see in details what's precisely changed since v1 please consult our or visit the .
Mobile Landings allow you to easily introduce continuity between your app, and your pushes: A user opening a push will be greeted by a rich message related to what they opened, rather than just ending up on your app's main menu.
They're included in the Startup, Business and Enterprise plans.
Please test your implementation using our and before releasing your app on the store. Make sure you're unwrapping your optionals!

















Touch ID/Passcode requirement
Foreground/Background action (whether tapping the button will open your app or perform the action in the background)
...

$labelBatchEventAttributespod 'Batch', '~> 2.0'github "BatchLabs/Batch-iOS-SDK" ~> 2.0There's no code required to make mobile landings work in automatic mode: just attach a landing to your push campaign, and Batch will display it.
You might want to go further into this documentation, and setup your delegate, or head to the Custom Actions documentation to add custom behaviour to buttons.
You may want to be in control of if, when and how landings will be loaded and displayed. Batch allows you to disable automatic displaying, and handle loading and displaying the view controller itself.
First, you'll need to implement UNUserNotificationCenterDelegate in a class (for more information, please see the Intercepting notifications part):
Then, you have to disable the automatic mode and set your class as your default UNUserNotificationCenter delegate:
Finally, you need to ask Batch to load the right view controller for the push payload (if applicable), and display it:
"Do Not Disturb" (DnD) feature: It allows you to tell Batch to hold on a mobile landing for you, rather than display it without using the fully manual mode. For example, if launching your app results in a splash screen or a fullscreen ad, you might find it undesirable to have Batch display something on top of it.
Turning on "Do Not Disturb" mode will make Batch enqueue the latest mobile landing, rather than display it.
Toggling DnD
Now, when you don't want Batch to automatically display, turn on Do Not Disturb:
Once you want to start showing landings automatically, call the method with false to turn it off.
Displaying pending mobile landings
After coming back from DnD mode, you might want to show the enqueued message, as Batch will not do that automatically. Batch exposes two properties/methods for managing the queue:
BatchMessaging.hasPendingMessage , allowing you to peek into the queue.
BatchMessaging.popPendingMessage() , allowing you to fetch the pending message (if any). Since calling this makes Batch delete its reference to it to save memory, further calls might return nil.
BatchMessaging.showPendingMessage() , allowing you to try to show the pending message, if any.
Here is a quick example of how they can be used:
Batch's messaging module supports setting up a delegate, which can be used for analytics:
It can be any object that implements the BatchMessagingDelegate protocol.
Batch can notify your delegate of lifecycle events of the in-app messages:
Custom button actions
In order to be able to use the "Custom" button action kind, you need to implement them using the Batch Actions module. More info here: Custom Actions
If you'd like to use a custom font instead of the system's, Batch allows you to override the fonts it will use:
The size will be overriden later, so you can use anything you want. Make sure you provide both a normal and a bold font, even if they are the same.
Nothing happens when I press an actionable button
Take a look at your application logs in Xcode, the SDK might try to warn you about an issue. Here are some the common messages and their probable cause:
Deeplinks on iOS are automatically called by the SDK using sharedApplication's openURL method. Since it needs a NSURL instance, the deeplink string needs to be a valid URL accepted by iOS' NSURL class. Please try again with a valid URL.
This can happen when you specified a custom action when creating the campaign on the dashboard, but the SDK couldn't execute it.
Make sure you always register your actions at every app start.

⚠️ Batch SDK 3.0 now requires Xcode 16.3 and iOS 15.0 or higher.
To upgrade from v2 to v3, you need to change the SDK version according to your package manager:
Update by using the "update package" option from Xcode or updating your package.swift.
.package(url: "https://github.com/BatchLabs/Batch-iOS-SDK", from: "3.0.1")Update your Podfile as following and run pod update.
pod 'Batch', '~> 3.0'Update your Cartfile if necessary and run carthage update Batch.
github "BatchLabs/Batch-iOS-SDK" ~> 3.0Batch SDK v3 adds compatibility for Mobile Landings with Push v2 which can be created from the Batch Dashboard's drag & drop composer.
The method batchInAppMessageReady has been removed from the BatchMessagingDelegate protocol. Please replace its usage with the new BatchInAppDelegate protocol.
Use the property access BatchMessaging.inAppDelegate to set your custom delegate.
So if you were doing:
You should now do:
For more information, please see the In-App messaging manual mode section.
The following methods in BatchMessagingDelegate have been removed:
batchMessageWasCancelledByAutoclose
batchMessageWasCancelledByUserAction
batchMessageWasCancelledByError
batchWebViewMessageDidTriggerAction
Please update your implementation to use the batchMessageDidDisappear:(NSString *_Nullable)messageIdentifier reason:(BatchMessagingCloseReason)reason; method. The new BatchMessagingCloseReason enum will provide the context about why the message was closed.
WebView actions will now trigger the updated batchMessageDidTriggerAction method, with the analyticsID of the action being passed as the ctaIdentifier.
The index (NSInteger) parameter in batchMessageDidTriggerAction has been replaced by ctaIdentifier (NSString). The method signature is now
For older MEP (Mobile Engagement Platform) messages, ctaIdentifier will be a string in the format "mepCtaIndex:" + index.
The constant BatchMessageGlobalActionIndex now returns a NSString instead of an NSInteger. Update any comparisons or usage accordingly.
So if you were doing:
You should now do:
For more information, please see the listening lifecycle event section.
To see in details what's precisely changed since V2 please consult our changelog or visit the APIs Reference.
BatchUser.installationID()[BatchUser installationID];class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// This can be called at any time, even before starting Batch.
BatchSDK.setEnablesFindMyInstallation(false)
// ...
}
}@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// This can be called at any time, even before starting Batch.
[BatchSDK setEnablesFindMyInstallation:false]
// ...
}class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// This can be called at any time, even before starting Batch.
// Enabling logs as early as possible ensures that you don't miss important events.
BatchSDK.setInternalLogsEnabled(true)
// ...
}
}@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// This can be called at any time, even before starting Batch.
// Enabling logs as early as possible ensures that you don't miss important events.
[BatchSDK setInternalLogsEnabled:true];
// ...
}if let debugVC = BatchSDK.makeDebugViewController() {
present(debugVC, animated: true, completion: nil)
}import SwiftUI
import Batch
struct BatchDebugView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
return BatchSDK.makeDebugViewController() ?? UIViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
struct ContentView: View {
@State var showBatchDebug = false
var body: some View {
VStack {
Button("Open Batch Debug") {
self.showBatchDebug = true
}.sheet(isPresented: self.$showBatchDebug) {
BatchDebugView()
}
}
}
}UIViewController *debugVC = [BatchSDK makeDebugViewController];
if (debugVC) {
[self presentViewController:debugVC animated:YES completion:nil];
}BatchPush.setNotificationsCategories(<your categories Set>)
BatchPush.registerForRemoteNotifications()[BatchPush setNotificationsCategories:<your categories NSSet>];
[BatchPush registerForRemoteNotifications];import UserNotifications
import Batch
@available(iOS 10.0, *)
@objc
class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
if response.actionIdentifier != UNNotificationDefaultActionIdentifier &&
response.actionIdentifier != UNNotificationDismissActionIdentifier {
// Perform your action here
}
BatchPush.handle(userNotificationCenter: center, didReceive: response)
completionHandler()
}
}@import UserNotifications;
@import Batch;
@interface NotificationCenterDelegateObjc : NSObject <UNUserNotificationCenterDelegate>
@end
@implementation NotificationCenterDelegateObjc
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
{
if (![UNNotificationDefaultActionIdentifier isEqualToString:response.actionIdentifier] &&
![UNNotificationDismissActionIdentifier isEqualToString:response.actionIdentifier]) {
// Handle your action here
}
[BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response];
completionHandler();
}
@endfunc application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
// Handle the action here
completionHandler()
}- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler
{
// Handle your action here
completionHandler();
}// iOS 8/9
// In your application delegate
func application(_ application: UIApplication,
handleActionWithIdentifier identifier: String?,
forRemoteNotification userInfo: [AnyHashable : Any],
completionHandler: @escaping () -> Void) {
if let identifier = identifier {
var args: [String : AnyObject] = [:]
if let payloadArgs = userInfo["action_args"] as? [String : AnyObject] {
args = payloadArgs
}
BatchActions.perform(actionIdentifier: identifier, arguments: args)
}
completionHandler()
}
// iOS 10
import UserNotifications
import Batch
@available(iOS 10.0, *)
@objc
class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
if response.actionIdentifier != UNNotificationDefaultActionIdentifier &&
response.actionIdentifier != UNNotificationDismissActionIdentifier {
// If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
var args: [String : AnyObject] = [:]
if let payloadArgs = response.notification.request.content.userInfo["action_args"] as? [String : AnyObject] {
args = payloadArgs
}
BatchActions.perform(actionIdentifier: response.actionIdentifier, arguments: args)
}
BatchPush.handle(userNotificationCenter: center, didReceive: response)
completionHandler()
}
}// iOS 8/9
// In your application delegate
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)userInfo
completionHandler:(void (^)())completionHandler
{
if (identifier) {
// If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
NSDictionary *args = @{};
NSObject *payloadArgs = userInfo[@"action_args"];
if ([payloadArgs isKindOfClass:[NSDictionary class]]) {
args = (NSDictionary*)payloadArgs;
}
[BatchActions performActionIdentifiedBy:identifier withArguments:args];
}
completionHandler();
}
// iOS 10
@import UserNotifications;
@import Batch;
@interface NotificationCenterDelegateObjc : NSObject <UNUserNotificationCenterDelegate>
@end
@implementation NotificationCenterDelegateObjc
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
{
if (![UNNotificationDefaultActionIdentifier isEqualToString:response.actionIdentifier] &&
![UNNotificationDismissActionIdentifier isEqualToString:response.actionIdentifier]) {
// If you want to add arguments to your action, this sample code will read from the "action_args" key of your custom payload
NSDictionary *args = @{};
NSObject *payloadArgs = response.notification.request.content.userInfo[@"action_args"];
if ([payloadArgs isKindOfClass:[NSDictionary class]]) {
args = (NSDictionary*)payloadArgs;
}
[BatchActions performActionIdentifiedBy:response.actionIdentifier withArguments:args];
}
[BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response];
completionHandler();
}
@endif #available(iOS 10, *) {
// We're on iOS 10
registerActions()
} else {
// We're on iOS 8/9
registerLegacyActions()
}if ([UNNotificationAction class]) {
// We're on iOS 10
[self registerActions];
} else {
// We're on iOS 8/9
[self registerLegacyActions];
}{"aps":{"category":"CATEGORY_NAME"}}BatchSDK.start(withAPIKey: "YOUR_API_KEY")[BatchSDK startWithAPIKey:@"MY_API_KEY"];BatchSDK.updateAutomaticDataCollection { config in
config.setGeoIPEnabled(true) // Enable GeoIP resolution on server side
config.setDeviceModelEnabled(true) // Enable automatic collection of the device model information
}[BatchSDK updateAutomaticDataCollection:^(BatchDataCollectionConfig * _Nonnull config) {
[config setGeoIPEnabled:true]; // Enable GeoIP resolution on server side
[config setDeviceModelEnabled:true]; // Enable automatic collection of the device model information
}];let editor = BatchUser.editor()
editor.setIdentifier("john.doe")
editor.setLanguage("en")
editor.setRegion("US")
try? editor.setEmail("john.doe@batch.com")
editor.setEmailMarketingSubscriptionState(BatchEmailSubscriptionState.subscribed)
editor.setAttribute(26, forKey:"age")
editor.removeAttribute(key:"firstname")
editor.addTag("has_bought", inCollection: "actions")
editor.removeTag("has_bought", fromCollection: "actions")
editor.clearTagCollection("actions")
editor.save()
BatchUserDataEditor *editor = [BatchUser editor];
[editor setIdentifier:@"john.doe"];
[editor setLanguage:@"en"];
[editor setRegion:@"US"];
[editor setEmail:@"john.doe@batch.com" error:nil];
[editor setEmailMarketingSubscriptionState:BatchEmailSubscriptionStateSubscribed];
[editor setIntegerAttribute:@26 forKey:@"age" error:nil];
[editor removeAttributeForKey:@"firstname"];
[editor addTag:@"has_bought" inCollection:@"actions"];
[editor removeTag:@"has_bought" fromCollection:@"actions"];
[editor clearTagCollection:@"actions"];
[editor save];BatchProfile.identify("john.doe")
BatchProfile.editor { editor in
try? editor.setLanguage("en")
try? editor.setRegion("US")
try? editor.setEmailAddress("john.doe@batch.com")
editor.setEmailMarketingSubscriptionState(BatchEmailSubscriptionState.subscribed)
try? editor.set(attribute: 26, forKey: "age")
try? editor.removeAttribute(key:"firstname")
try? editor.addToStringArray(item: "has_bought", forKey: "actions")
try? editor.removeFromStringArray(item: "has_bought", forKey: "actions")
try? editor.removeAttribute(key:"actions")
// Don't forget to use `editor.save()` if you use the editor in a local variable and not a closure like here.
}[BatchProfile identify:@"john.doe"];
[BatchProfile editWithBlock:^(BatchProfileEditor * _Nonnull editor) {
[editor setLanguage:@"en" error:nil];
[editor setRegion:@"US" error:nil];
[editor setEmailAddress:@"john.doe@batch.com" error:nil];
[editor setEmailMarketingSubscriptionState:BatchEmailSubscriptionStateSubscribed];
[editor setIntegerAttribute:26 forKey:@"age" error:nil];
[editor removeAttributeForKey:@"firstname" error:nil];
[editor addItemToStringArrayAttribute:@"has_bought" forKey:@"has_bought" error:nil];
[editor removeItemFromStringArrayAttribute:@"has_bought" forKey:@"has_bought" error:nil];
[editor removeAttributeForKey:@"actions" error:nil];
// Don't forget to use `[editor save]' if you use the editor in a local variable and not a closure like here.
}];import Batch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Disable profile's migration
BatchSDK.setDisabledMigrations([
// Whether Batch should automatically identify logged-in user when running the SDK for the first time.
// This mean user with a custom_user_id will be automatically attached a to a Profile and could be targeted within a Project scope.
.customID,
// Whether Batch should automatically attach current installation's data (language/region/customDataAttributes...)
// to the User's Profile when running the SDK for the first time.
.customData
])
// Then start Batch SDK.
BatchSDK.start(withAPIKey: "YOUR_API_KEY")
}@import Batch;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Disable profile's migration
[BatchSDK setDisabledMigrations:
// Whether Batch should automatically identify logged-in user when running the SDK for the first time.
// This mean user with a custom_user_id will be automatically attached a to a Profile and could be targeted within a Project scope.
BatchMigrationCustomID |
// Whether Batch should automatically attach current installation's data (language/region/customDataAttributes...)
// to the User's Profile when running the SDK for the first time.
BatchMigrationCustomData
];
// Then start Batch SDK.
[BatchSDK startWithAPIKey:@"MY_API_KEY"];
return YES;
}let data = BatchEventData()
data.add(tag: "squash")
data.add(tag: "daily_digest")
data.put(true, forKey: "premium")
data.put("123456", forKey: "id")
BatchUser.trackEvent("read_article", withLabel: "sports", data: data)BatchEventData *data = [BatchEventData new];
[data addTag:@"squash"];
[data addTag:@"daily_digest"];
[data putBool:@(YES) forKey:@"premium"];
[data putString:@"123456" forKey:@"id"];
[BatchUser trackEvent:@"read_article" withLabel:@"sports" associatedData:data];let attributes = BatchEventAttributes { data in
data.put("sports", forKey: "$label")
data.put(["squash", "daily_digest"], forKey: "$tags")
data.put("123456", forKey: "id")
data.put(true, forKey: "premium")
}
BatchProfile.trackEvent(name: "read_article", attributes: attributes)BatchEventAttributes *attributes = [BatchEventAttributes new];
[attributes putString:@"sports" forKey:@"$label"];
[attributes putStringArray:@[@"squash", @"daily_digest"] forKey:@"$tags"];
[attributes putString:@"123456" forKey:@"id"];
[attributes putBool:YES forKey:@"premium"];
[BatchProfile trackEventWithName:@"read_article" attributes:attributes];@objc
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
AppDelegate.tryShowBatchMessage(userInfo: response.notification.request.content.userInfo as [NSObject : AnyObject])
BatchPush.handle(userNotificationCenter: center, didReceive: response)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
AppDelegate.tryShowBatchMessage(userInfo: notification.request.content.userInfo as [NSObject : AnyObject])
BatchPush.handle(userNotificationCenter: center, willPresent: notification, willShowSystemForegroundAlert: true)
completionHandler([.sound, .alert, .badge])
}
}// NotificationDelegate.h
@import Foundation;
@import UserNotifications;
@interface NotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
@end
// NotificationDelegate.m
#import "NotificationDelegate.h"
@import Batch;
@implementation NotificationDelegate
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler {
[AppDelegate tryShowBatchMessage:response.notification.request.content.userInfo];
[BatchPush handleUserNotificationCenter:center didReceiveNotificationResponse:response];
completionHandler();
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
[AppDelegate tryShowBatchMessage:response.notification.request.content.userInfo];
[BatchPush handleUserNotificationCenter:center willPresentNotification:notification willShowSystemForegroundAlert:YES];
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound)
}
@endclass AppDelegate: UIResponder, UIApplicationDelegate {
let notificationDelegate = NotificationDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.setAutomaticMode(on: false)
UNUserNotificationCenter.current().delegate = notificationDelegate
}
}@interface AppDelegate ()
{
NotificationDelegate *notificationDelegate;
}
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[BatchMessaging setAutomaticMode:NO];
if ([UNUserNotificationCenter class]) {
notificationDelegate = [NotificationDelegate new];
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:notificationDelegate];
}
}
@endstatic func tryShowBatchMessage(userInfo: [NSObject : AnyObject]) {
// Put the display code in this if block if you don't want the messaging to show when the user is already using the app
// if UIApplication.shared.applicationState == .inactive {
// }
guard let message = BatchMessaging.message(fromPushPayload: userInfo) else {
// No message from push payload
return
}
guard let vc = try? BatchMessaging.loadViewController(for:message) else {
print("An error occurred while loading Batch's messaging view")
return
}
// Let Batch present it itself
BatchMessaging.present(vc)
// Or,
// if you want to display the message yourself, you will need to check if the VC should be presented in
// its own window, to allow user interaction around the message if needed. Banners require this, for example.
guard let batchVC = vc as? BatchMessagingViewController else {
print("The loaded VC does not satisfy the BatchMessagingViewController protocol like it should")
return
}
if (batchVC.shouldDisplayInSeparateWindow) {
// Display in your own UIWindow
} else {
var targetVC = UIApplication.shared.keyWindow?.rootViewController
if let presentedVC = targetVC?.presentedViewController {
targetVC = presentedVC
}
targetVC?.present(vc, animated: true, completion: nil)
}
}+ (void)tryShowBatchMessage:(NSDictionary *)userInfo
{
if (!userInfo) {
return;
}
// Put the display code in this if block if you don't want the messaging to show when the user is already using the app
/*
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateInactive) {
}
*/
BatchMessage *message = [BatchMessaging messageFromPushPayload:userInfo];
if (message) {
// You can show a loading view in the meantime for a better user experience
NSError *err = nil;
UIViewController *vc = [BatchMessaging loadViewControllerForMessage:message error:&err];
if (err) {
NSLog(@"An error occurred while loading Batch's messaging view: %@", [err localizedDescription]);
} else if (vc) {
// Let Batch present it itself
[BatchMessaging presentMessagingViewController:vc];
// Or,
// if you want to display the message yourself, you will need to check if the VC should be presented in
// its own window, to allow user interaction around the message if needed. Banners require this, for example.
if ([vc conformsToProtocol:@protocol(BatchMessagingViewController)]) {
if (((id<BatchMessagingViewController>)vc).shouldDisplayInSeparateWindow) {
// Show this VC in your own UIWindow
} else {
UIViewController *targetVC = [[[UIApplication sharedApplication] keyWindow] rootViewController];
UIViewController *presentedVC = targetVC.presentedViewController;
if (presentedVC) {
targetVC = presentedVC;
}
[targetVC presentViewController:vc animated:YES completion:nil];
}
}
}
}
}
BatchMessaging.doNotDisturb = trueBatchMessaging.doNotDisturb = YES;func splashScreenDidDisappear() {
BatchMessaging.doNotDisturb = false
BatchMessaging.showPendingMessage()
}- (void)splashScreenDidDisappear {
BatchMessaging.doNotDisturb = NO;
[BatchMessaging showPendingMessage];
}
var messagingDelegate: SampleBatchMessagingDelegate?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
[...]
messagingDelegate = SampleBatchMessagingDelegate()
BatchMessaging.delegate = messagingDelegate
[...]
}// Header file
@property (strong, nonatomic) SampleBatchMessagingDelegate *messagingDelegate;
// Implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[...]
// Delegates are weak so be sure to keep a reference to it
self.messagingDelegate = [SampleBatchMessagingDelegate new];
[BatchMessaging setDelegate:self.messagingDelegate];
[...]
}import Foundation
import Batch.Messaging
public class SampleBatchMessagingDelegate: NSObject, BatchMessagingDelegate {
public func batchMessageDidAppear(messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageDidAppear: \(messageIdentifier)")
}
public func batchMessageDidTriggerAction(_ action: BatchMessageAction, messageIdentifier identifier: String?, actionIndex index: Int) {
// A button or global tap action has been triggered
// If the action is a CTA (button), you can fetch the label
if let cta = action as? BatchMessageCTA {
let label = cta.label
// [...]
}
// CTAs are allowed to simply be a "dismiss" action, in which case they
// will not have any associated action name and arguments
if !action.isDismissAction() {
let actionName = action.action // not null
// [...]
}
}
public func batchMessageDidDisappear(_ messageIdentifier: String?, reason: BatchMessagingCloseReason) {
print("SampleBatchMessagingDelegate - batchMessageDidDisappear: \(messageIdentifier) with reason: \(reason)"
}
}// Header file (.h)
@import Foundation;
@import Batch.Messaging;
@interface SampleBatchMessagingDelegate : NSObject <BatchMessagingDelegate>
@end
// Implementation file (.m)
#import "SampleBatchMessagingDelegate.h"
@implementation SampleBatchMessagingDelegate
- (void)batchMessageDidAppear:(NSString* _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidAppear: %@", messageIdentifier);
}
- (void)batchMessageDidDisappear:(NSString* _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidDisappear: %@", messageIdentifier);
}
@end// Set a custom font
let font = UIFont(name: "MyFont", size: 10)
let boldFont = UIFont(name: "MyFont-Bold", size: 10)
BatchMessaging.setFontOverride(font, boldFont: boldFont)
// or variant with italic and boldItalic
// BatchMessaging.setFontOverride(UIFont?, boldFont: UIFont?, italicFont: UIFont?, boldItalicFont: UIFont?)
// Clear the custom font, and use the system one
BatchMessaging.setFontOverride(nil, boldFont: nil)// Set a custom font
UIFont* font = [UIFont fontWithName:@"MyFont" size: 10];
UIFont* boldFont = [UIFont fontWithName:@"MyFont-Bold" size: 10];
[BatchMessaging setFontOverride:font boldFont:boldFont];
// Clear the custom font, and use the system one
[BatchMessaging setFontOverride:nil boldFont:nil];An error occured while making a NSURL for the following link: '<your deeplink>', ignoring deeplink action.The action 'ACTION NAME' couldn't be found. Did you forget to register it?// Application delegate
var inAppMsgDelegate = InAppMsgDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.delegate = inAppMsgDelegate
}
// InAppMsgDelegate implementation
@objc
class InAppMsgDelegate: NSObject, BatchMessagingDelegate {
func batchInAppMessageReady(message: BatchInAppMessage) {
// Your implementation
}
}
// Application delegate
@property InAppMsgDelegate *inAppMsgDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.inAppMsgDelegate = [InAppMsgDelegate new];
BatchMessaging.delegate = self.inAppMsgDelegate;
}
// InAppMsgDelegate implementation
@interface InAppMsgDelegate : NSObject <BatchMessagingDelegate>
@end
@implementation InAppMsgDelegate
- (void)batchInAppMessageReady:(nonnull BatchInAppMessage*)message
{
// Your implementation
}
@end
// Application delegate
var inAppMsgDelegate = InAppMsgDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.inAppDelegate = inAppMsgDelegate
}
// InAppMsgDelegate implementation
@objc
class InAppMsgDelegate: NSObject, BatchInAppDelegate {
func batchInAppMessageReady(message: BatchInAppMessage) {
// Your implementation
}
}
// Application delegate
@property InAppMsgDelegate *inAppMsgDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.inAppMsgDelegate = [InAppMsgDelegate new];
BatchMessaging.inAppDelegate = self.inAppMsgDelegate;
}
// InAppMsgDelegate implementation
@interface InAppMsgDelegate : NSObject <BatchInAppDelegate>
@end
@implementation InAppMsgDelegate
- (void)batchInAppMessageReady:(nonnull BatchInAppMessage*)message
{
// Your implementation
}
@end
// Application delegate
var messagingDelegate = MsgDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.delegate = inAppMsgDelegate
}
// BatchMessagingDelegate implementation
class SampleBatchMessagingDelegate: NSObject, BatchMessagingDelegate {
public func batchMessageDidAppear(messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageDidAppear: \(messageIdentifier)")
}
public func batchMessageDidTriggerAction(_ action: BatchMessageAction, messageIdentifier identifier: String?, actionIndex index: Int) {
print("SampleBatchMessagingDelegate - batchMessageDidTriggerAction: \(messageIdentifier)")
}
public func batchWebViewMessageDidTriggerAction(_ action: BatchMessageAction?, messageIdentifier: String?, analyticsIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchWebViewMessageDidTriggerAction \(String(describing:action)), tracking ID: \(messageIdentifier ?? "<nil>"), analyticsIdentifier: \(analyticsIdentifier ?? "<nil>")")
}
public func batchMessageWasCancelledByAutoclose(_ messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByAutoclose: \(messageIdentifier)")
}
public func batchMessageWasCancelledByUserAction(_ messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByUserAction: \(messageIdentifier)")
}
public func batchMessageWasCancelledByError(_ messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByError: \(messageIdentifier)")
}
public func batchMessageDidDisappear(messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageDidDisappear: \(messageIdentifier)")
}
}// AppDelegate.m
@property SampleBatchMessagingDelegate *messagingDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.messagingDelegate = [SampleBatchMessagingDelegate new];
BatchMessaging.delegate = self.messagingDelegate;
}
// Header file (.h)
@interface SampleBatchMessagingDelegate : NSObject <BatchMessagingDelegate>
@end
// Implementation file (.m)
@implementation SampleBatchMessagingDelegate
- (void)batchMessageDidAppear:(NSString* _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidAppear: %@", messageIdentifier);
}
- (void)batchMessageDidTriggerAction:(BatchMessageAction * _Nonnull)action messageIdentifier:(NSString *)identifier actionIndex:(NSInteger)index
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidTriggerAction: %@", messageIdentifier);
}
- (void)batchWebViewMessageDidTriggerAction:(BatchMessageAction * _Nonnull)action messageIdentifier:(NSString *)identifier analyticsIdentifier:(NSString *)analyticsIdentifierex
{
NSLog(@"SampleBatchMessagingDelegate - batchWebViewMessageDidTriggerAction: %@", messageIdentifier);
}
- (void)batchMessageWasCancelledByAutoclose:(NSString * _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchWebVbatchMessageWasCancelledByAutocloseiewMessageDidTriggerAction: %@", messageIdentifier);
}
- (void)batchMessageWasCancelledByUserAction:(NSString * _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageWasCancelledByUserAction: %@", messageIdentifier);
}
- (void)batchMessageDidDisappear:(NSString* _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidDisappear: %@", messageIdentifier);
}
@end// Application delegate
var messagingDelegate = MsgDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
BatchMessaging.delegate = messagingDelegate
}
// BatchMessagingDelegate implementation
class SampleBatchMessagingDelegate: NSObject, BatchMessagingDelegate {
public func batchMessageDidAppear(messageIdentifier: String?) {
print("SampleBatchMessagingDelegate - batchMessageDidAppear: \(messageIdentifier)")
}
public func batchMessageDidTriggerAction(_ action: BatchMessageAction, messageIdentifier identifier: String?, ctaIdentifier: String) {
print("SampleBatchMessagingDelegate - batchMessageDidTriggerAction: \(messageIdentifier)")
// For MEP messages, ctaIdentifier might be "mepCtaIndex:0", "mepCtaIndex:1", etc.
}
public func batchMessageDidDisappear(_ messageIdentifier: String?, reason: BatchMessagingCloseReason) {
switch(reason) {
case .action:
print("SampleBatchMessagingDelegate - batchMessageDidDisappear: \(messageIdentifier)")
case .auto:
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByAutoclose: \(messageIdentifier)")
case .user:
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByUserAction: \(messageIdentifier)")
case .error:
print("SampleBatchMessagingDelegate - batchMessageWasCancelledByError: \(messageIdentifier)")
}
}
}// AppDelegate.m
@property SampleBatchMessagingDelegate *messagingDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.messagingDelegate = [SampleBatchMessagingDelegate new];
BatchMessaging.delegate = self.messagingDelegate;
}
// Header file (.h)
@interface SampleBatchMessagingDelegate : NSObject <BatchMessagingDelegate>
@end
// Implementation file (.m)
@implementation SampleBatchMessagingDelegate
- (void)batchMessageDidAppear:(NSString* _Nullable)messageIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidAppear: %@", messageIdentifier);
}
- (void)batchMessageDidTriggerAction:(BatchMessageAction * _Nonnull)action messageIdentifier:(NSString *)identifier ctaIdentifier:(NSString *_Nonnull)ctaIdentifier
{
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidTriggerAction: %@", messageIdentifier);
// For MEP messages, ctaIdentifier might be "mepCtaIndex:0", "mepCtaIndex:1", etc.
}
- (void)batchMessageDidDisappear:(NSString* _Nullable)messageIdentifier reason:(BatchMessagingCloseReason)reason
{
switch (reason) {
case BatchMessagingCloseReasonAction:
NSLog(@"SampleBatchMessagingDelegate - batchMessageDidDisappear: %@", messageIdentifier ?: @"<nil>");
break;
case BatchMessagingCloseReasonAuto:
NSLog(@"SampleBatchMessagingDelegate - batchMessageWasCancelledByAutoclose: %@", messageIdentifier ?: @"<nil>");
break;
case BatchMessagingCloseReasonUser:
NSLog(@"SampleBatchMessagingDelegate - batchMessageWasCancelledByUserAction: %@", messageIdentifier ?: @"<nil>");
break;
case BatchMessagingCloseReasonError:
NSLog(@"SampleBatchMessagingDelegate - batchMessageWasCancelledByError: %@", messageIdentifier ?: @"<nil>");
break;
}
}
@endbatchMessageDidTriggerAction:(BatchMessageAction *_Nonnull)action
messageIdentifier:(NSString *_Nullable)identifier
ctaIdentifier:(NSString *_Nonnull)ctaIdentifier;It is possible to get the raw payload of a notification (which includes your 'custom payload'), would you wish to do something with it. This is done using standard iOS APIs:
It's recommended to implement the UNUserNotificationCenterDelegate in a class.
It allows you to:
Unify local and remote notifications callbacks
Get the same callback for all user interactions with a push (open, dismiss and actions), even for a cold start
Get notified of notifications received while your app is in the foreground. This allows you to take immediate action or to tell iOS to display the push as if your app was opened in background.
You'll also need to call Batch in its two methods to make sure all of the SDK and dashboard's features work correctly. Keep in mind that being a delegate, it will need to be retained by a variable, since iOS only weakly retains it.
Here's a sample implementation:
@objc
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
// NotificationDelegate.h
@import Foundation;
@import UserNotifications;
@interface NotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
@end
// NotificationDelegate.m
#import "NotificationDelegate.h"
@import Batch;
Finally, set this class as your default UNUserNotificationCenter delegate:
Handling a custom payload
The custom payload is merged at the root of the userInfo you get when called back by iOS:
If your app supports background refresh, it's recommended to implement this method:
Don't forget to call the completionHandler with an appropriate value. Batch will only do so for you when it encounters a deeplink.
Otherwise, if you support iOS 6 devices or don't implement background refresh, please implement the older notification delegate method. Note that both can be implemented for backward-compatibility, you'll only be called on one of them.
Your custom payload will be in the userInfo dictionary.
Handling a custom payload
The custom payload is merged at the root of the userInfo you get when called back by iOS:
By default, Batch will automatically try to open the deeplink you've set in your push campaigns.
If you'd like to prevent Batch from doing that while still being able to use deeplinks in push campaigns, you can call the following method in applicationDidFinishLaunchingWithOptions:
Then, you can ask Batch to give you the deeplink from the notification when you get it:
Be careful, this method will return nil if a deeplink could not be found.
class AppDelegate: UIResponder, UIApplicationDelegate {
let notificationDelegate = NotificationDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10, *) {
UNUserNotificationCenter.current().delegate = notificationDelegate
}
}
}@interface AppDelegate ()
{
NotificationDelegate *notificationDelegate;
}
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UNUserNotificationCenter class]) {
notificationDelegate = [NotificationDelegate new];
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:notificationDelegate];
}
}
@end// Works with both methods
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// When reading the custom payload, you should perform extensive type checking to prevent crashes if you
// ever change a type, be it intentionally or accidentally.
// Root keys will be of the type you used in your source JSON, unlike Android.
// Please see NSJSONSerialization for more info about how the JSON will be interpreted by iOS
// Here we'll read the "article_id" key of the following custom payload : {"article_id": 2}
if let articleID = userInfo["article_id"] as? Int {
// Handle your article ID
}
// If you have more complex objets, they also should be parsed correctly
// Matching custom payload: {"user_data": {"id": 2}}
if let userData = userInfo["user_data"] as? [AnyHashable : Any],
let userID = userData["id"] as? Int {
// Handle your user ID
}
}// Works with both methods
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
NSDictionary *userInfo = response.notification.request.content.userInfo
// When reading the custom payload, you should perform extensive type checking to prevent crashes if you
// ever change a type, be it intentionally or accidentally.
// Root keys will be of the type you used in your source JSON, unlike Android.
// Please see NSJSONSerialization for more info about how the JSON will be interpreted by iOS
// Here we'll read the "article_id" key of the following custom payload : {"article_id": 2}
NSNumber *articleID = userInfo[@"article_id"];
if ([articleID isKindOfClass:[NSNumber class]]) {
// Handle your article ID
}
// If you have more complex objets, they also should be parsed correctly
// Matching custom payload: {"user_data": {"id": 2}}
NSDictionary *userData = userInfo[@"user_data"];
if ([userData isKindOfClass:[NSDictionary class]]) {
NSNumber userID = userData[@"id"];
if ([userID isKindOfClass:[NSNumber class]]) {
// Handle your user ID
}
}
} func application(application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;func application(application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any])- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;// Works with both methods
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
// When reading the custom payload, you should perform extensive type checking to prevent crashes if you
// ever change a type, be it intentionally or accidentally.
// Root keys will be of the type you used in your source JSON, unlike Android.
// Please see NSJSONSerialization for more info about how the JSON will be interpreted by iOS
// Here we'll read the "article_id" key of the following custom payload : {"article_id": 2}
if let articleID = userInfo["article_id"] as? Int {
// Handle your article ID
}
// If you have more complex objets, they also should be parsed correctly
// Matching custom payload: {"user_data": {"id": 2}}
if let userData = userInfo["user_data"] as? [AnyHashable : Any],
let userID = userData["id"] as? Int {
// Handle your user ID
}
}// Works with both methods
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
// When reading the custom payload, you should perform extensive type checking to prevent crashes if you
// ever change a type, be it intentionally or accidentally.
// Root keys will be of the type you used in your source JSON, unlike Android.
// Please see NSJSONSerialization for more info about how the JSON will be interpreted by iOS
// Here we'll read the "article_id" key of the following custom payload : {"article_id": 2}
NSNumber *articleID = userInfo[@"article_id"];
if ([articleID isKindOfClass:[NSNumber class]]) {
// Handle your article ID
}
// If you have more complex objets, they also should be parsed correctly
// Matching custom payload: {"user_data": {"id": 2}}
NSDictionary *userData = userInfo[@"user_data"];
if ([userData isKindOfClass:[NSDictionary class]]) {
NSNumber userID = userData[@"id"];
if ([userID isKindOfClass:[NSNumber class]]) {
// Handle your user ID
}
}
}BatchPush.enableAutomaticDeeplinkHandling(false)[BatchPush enableAutomaticDeeplinkHandling:NO];func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
let deeplink = BatchPush.deeplink(fromUserInfo: userInfo)
}- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSString *deeplink = [BatchPush deeplinkFromUserInfo:userInfo];
}Compiles with Xcode 26.1.1
Batch requires Xcode 26.1.1 and iOS 15.0 or higher (iOS 16.0 or higher for Mac Catalyst only)
Core
Reintroduced the arm64e architecture slice since the previous removal was a temporary measure due to App Store/TestFlight validation issues observed with arm64e slices compiled using early iOS 18 SDKs.
Profile
Profile string attributes now support up to 300 characters for the Customer Engagement Platform (CEP). The limit for the Mobile Engagement Platform (MEP) remains 64 characters. Attributes set via BatchProfileEditor.setStringAttribute() longer than 64 characters will only be applied to the CEP.
Event string attributes now support up to 300 characters for the Customer Engagement Platform (CEP). The limit for the Mobile Engagement Platform (MEP) remains 200 characters. Attributes set via BatchEventAttributes.putString() longer than 200 characters will only be applied to the CEP.
Compiles with Xcode 16.4 Batch requires Xcode 16.4 and iOS 15.0 or higher
Core
Fixed an issue where some GIF files would play too fast
Fixed an issue where didRegisterForRemoteNotificationsWithDeviceToken/didFailToRegisterForRemoteNotificationsWithError UIApplicationDelegate methods broke when swizzling was enabled on a SwiftUI lifecycle application.
Messaging
Introduced In-App Automations within the Customer Engagement Platform.
Introduced Spacer component for creating flexible empty space within In-App messages.
Added support for In-App WebView format within the Customer Engagement Platform.
Added fill
Compiles with Xcode 16.3 Batch requires Xcode 16.3 and iOS 15.0 or higher
This version is fully compatible with iOS 26 and Xcode 26
Core
Re-release of 3.0.2 due to a build script issue.
Compiles with Xcode 16.3 Batch requires Xcode 16.3 and iOS 15.0 or higher
Core
Important Xcode 26 warning: We have had reports of the SDK failing to pass App Store/Testflight validation due to the presence of an arm64e slice compiled with the iOS 18 SDK. Therefore, we removed the arm64e slice for the time being, planning to reintroduce it at a later date when we can compile with a stable iOS 26 SDK. Please contact our support team if this is a problem for you.
Messaging
Fixed an issue where In-App messages might not display during NEW SESSION events.
Compiles with Xcode 16.3. Batch requires Xcode 16.3 and iOS 15.0 or higher
Core
Re-release of 3.0.0 due to a misalignment of deployment targets across package managers
This is a major release, please see our for more info on how update your current Batch implementation.
Compiles with Xcode 16.3. Batch requires Xcode 16.3 and iOS 15.0 or higher
Core
Added NS_SWIFT_UI_ACTOR annotations on a couple of methods to prevent threading related bugs. While this is a breaking change, most implementations won't notice anything.
Messaging
Added Mobile Landings with Push v2 which can be created from the CEP composer in the Batch Dashboard.
Added new BatchInAppDelegate protocol and inAppDelegate property on BatchMessaging, in order to handler In-App messages manually.
Removed individual close callback methods (batchMessageWasCancelledByAutoclose, batchMessageWasCancelledByUser
Compiles with Xcode 16.0 Batch requires Xcode 16.0 and iOS 13.0 or higher
This version is fully compatible with iOS 26 and Xcode 26
Core
Added support for Xcode 26.
Removed arm64e slice.
Fixed an issue where didRegisterForRemoteNotificationsWithDeviceToken/didFailToRegisterForRemoteNotificationsWithError UIApplicationDelegate methods broke when swizzling was enabled on a SwiftUI lifecycle application.
Compiles with Xcode 16.0 Batch requires Xcode 16.0 and iOS 13.0 or higher
Core
Re-release of 2.1.0 with a valid codesignature.
Compiles with Xcode 16.0 Batch requires Xcode 16.0 and iOS 13.0 or higher
Profile
Added setPhoneNumber API to the BatchProfileEditor class. This requires to have a user identifier registered or to call the identify method beforehand.
Added setSMSMarketingSubscriptionState API to the BatchProfileEditor class.
Compiles with Xcode 15.3 Batch requires Xcode 15.3 and iOS 13.0 or higher
Core
Due to CocoaPod bugs, we removed Code Signing and Mergable Library support from Batch when installed via CocoaPods. If those are important to you, please use Swift Package Manager or manually integrate the framework.
Compiles with Xcode 15.3 Batch requires Xcode 15.3 and iOS 13.0 or higher
Event
Fixed an issue where tracking an event without custom attributes wasn't sent.
This is a major release, please see our for more info on how update your current Batch implementation.
Batch requires Xcode 15.3 and iOS 13.0 or higher
Linking
Dropped support for iOS 12 and lower.
Batch is now distributed as a dynamic framework. You can now safely link Batch between multiple framework targets!
Batch is now a . The additional metadata induced by this feature makes the XCFramework distribution heavier, but has no effect in the final size of Batch in your app once compiled.
visionOS
This version introduces visionOS support. Some features are still unsupported, like:
In-App messaging and mobile landings are unavailable.
In-App rating is not supported on visionOS due to an OS limitation.
Core
All deprecated APIs in the SDK v1 have been removed and some others have been renamed/reworked to feel Swift native.
The Batch class has been renamed to BatchSDK.
Batch has dropped the support for IDFA. You can no longer set an advertising id to Batch and all related APIs have been removed.
Removed deprecated method setUseIDFA which was a no-op.
Push
Removed deprecated method setupPush, which was a no-op.
Removed the ability to register notification categories using Batch. Please use the .
Removed deprecated method registerForRemoteNotifications.
User
Most of all user's related APIs have been removed and replaced with their equivalent in the new BatchProfile module. Only the reading methods are still present for backward-compatibility but keep in mind these methods are only about the installation data and not your Profile since it may be updated from another source.
Removed deprecated class BatchUserProfile and all related methods/properties.
Removed deprecated method trackEvent:withLabel:data and all other variants. You should now use the BatchProfile.trackEvent APIs.
Removed method BatchUser.editor and the related class BatchUserDataEditor
Event
This version introduced two new types of attribute that can be attached to an event : Array and Object.
Renamed BatchEventData class to BatchEventAttributes
Removed deprecated method Batch.trackEvent(String event, String label, JSONObject data).
Removed all trackEvent APIs from the user module. You should now use Batch.Profile.trackEvent
Messaging
Removed method setDelegate, which has been replaced by the delegate property.
Removed method setCanReconfigureAVAudioSession, which has been replaced by the canReconfigureAVAudioSession property.
Removed method setAutomaticMode
Inbox
Removed deprecated property title from BatchInboxNotificationContent. You should access it from the message property.
Removed deprecated property body from BatchInboxNotificationContent. You should access it from the message property.
Profile
Introduced BatchProfile, a new module that enables interacting with profiles. Its functionality replaces most of BatchUser used to do.
Added identify API as replacement of BatchUser.editor().setIdentifier.
Added editor method to get a new instance of a BatchProfileEditor as replacement of BatchUserDataEditor.
Added
Fixes
Fixed an issues where the thread sanitizer would report an issue when opening deeplinks.
Compiles with Xcode 15.1 Batch requires Xcode 15.1 and iOS 12.0 or higher
Core
Re-release of 1.21.2 with a valid codesignature. Note: 1.21.3 only exists on SPM & Carthage. The 1.21.2 Cocoapods artifact & manual download has been updated with the new signature.
Compiles with Xcode 15.1 Batch requires Xcode 15.1 and iOS 12.0 or higher
Core
Re-release of 1.21.1 due to a build script issue.
Compiles with Xcode 15.1 Batch requires Xcode 15.1 and iOS 12.0 or higher
Core
Batch now sets a MinimumOSVersion of 100.0 to workaround a framework validation bug on Xcode 15.3.
Compiles with Xcode 15.1 Batch requires Xcode 15.1 and iOS 12.0 or higher
Core
Improved Batch Logger for better use in Xcode 15.
Removed support for IDFA (Advertising ID) automatic collection. Batch.setCanUseIDFA is now a no-op.
You need to collect it from your side and pass it to Batch via the added [BatchUserDataEditor setAttributionIdentifier:] method.
Added Batch's Privacy Manifest. Since, as of writing, Apple does not support the XCPrivacy file for static frameworks you may not see Batch in the genertaed privacy report.
In-App
In-App WebViews can now be inspected using macOS Safari on iOS 16.4 and higher.
Fixed a crash when trying to display 0px width images in the Image format.
Compiles with Xcode 14.3.1 Batch requires Xcode 14.3.1 and iOS 11.0 or higher
Note: Due to Xcode 15 dropping support for iOS 11.0, this is the last minor version supporting iOS 11.
Core
Fixed Universal Links not triggering SwiftUI's' onOpenUrl (iOS 14+). For iOS 13, implement your own UISceneDelegate using a custom application delegate or use a BatchDeeplinkDelegate.
User
Added method [BatchUserDataEditor setEmail:]. This requires to have a user identifer registered or to call the setIdentifier method on the editor instance beforehand.
Added [BatchUserDataEditor setEmailMarketingSubscriptionState:] method to subscribe to the email marketing list.
Inbox
Added the ability to know if a BatchPushMessage has been displayed from the inbox via the isDisplayedFromInbox property.
Added the ability to know if a notification has a landing message attached on BatchInboxNotificationContent using the hasLandingMessage() method.
Added the ability to display a landing message attached to a BatchInboxNotificationContent
Messaging
Added support for Dynamic Type allowing Batch to adapt textual content to honor the user's font size settings. This is enabled by default, you can disable it at any moment by using [BatchMessaging setEnableDynamicType:false].
In-Apps Banner no longer displays when the presented view controller is an SFSafariViewController as it should not be hidden or obscured by other views or layers.
Compiles with Xcode 13.3.1 Batch requires Xcode 13.3.1 and iOS 10.0 or higher
Note: Due to Xcode 14 dropping support for Bitcode and 32-bit architectures, this is the last minor version to be bundled with these slices.
Messaging
In-App WebView can now display inline <video> elements.
Compiles with Xcode 13.3.1 Batch requires Xcode 13.3.1 and iOS 10.0 or higher
Note: Due to Xcode 14 dropping support for Bitcode and 32-bit architectures, this is the last minor version to be bundled with these slices.
Core
Added support for Universal Links in apps using UISceneDelegate
Messaging
In-App WebView will now ignore WebKit error (204) when loading a video URL without HTML container
Compiles with Xcode 13.3.1 Batch requires Xcode 13.3.1 and iOS 10.0 or higher
Note: Due to Xcode 14 dropping support for Bitcode and 32-bit architectures, this is the last minor version to be bundled with these slices.
Core
Fixed a SQLite crash caused by a race condition: triggering an In-app message with "Re-evaluate campaign eligibility" enabled right after calling [BatchUserDataEditor save] could make concurrent accesses to SQLite and crash.
Messaging
Fixed a rare crash that could happen when re-evaluating multiple campaigns eligibility for a same trigger.
Compiles with Xcode 13.3.1 Batch requires Xcode 13.3.1 and iOS 10.0 or higher
Note: Due to Xcode 14 dropping support for Bitcode and 32-bit architectures, this is the last minor version to be bundled with these slices.
Core
Fixed implementations of various internal objects implementing NSCopying, which could crash in some rare cases on pre iOS 13 devices.
Fixed an issue the SDK would not send a notification permission status change to Batch's servers until a later SDK start.
Compiles with Xcode 13.3.1 Batch requires Xcode 13.3.1 and iOS 10.0 or higher
Note: Due to Xcode 14 dropping support for Bitcode and 32-bit architectures, this is the last minor version to be bundled with these slices.
Core
Fixed an issue where some non-profile data (display receipts and In-App campaign view history) wasn't deleted after using [Batch optOutAndWipeData].
Updated BatchMessagingContentType's definition to use a modern NS_ENUM declaration. Fixes an issue where the header could not be parsed in an Objective-C++ context.
Messaging
Fixed a display issue on Modals with an image but no title.
Compiles with Xcode 13.3 Batch requires Xcode 13 and iOS 10.0 or higher
Event Dispatchers
Added name and version on the BatchEventDispatcherDelegate protocol. There is no need to implement those in your implementations. If you are using a Batch dispatcher plugin, please update it.
Messaging
In-App campaigns are now synchronized on SDK start without delay and will fallback on local cache. This changes the way the "next session" trigger works, which is now equivalent to what used to be "as soon as possible".
Added just-in-time verification for In-App campaigns.
Inbox
Added the ability to disable the filtering of silent notifications on BatchInboxFetcher using the filterSilentNotifications property.
To support this feature, we deprecated the title and body properties on BatchInboxNotificationContent and moved them under the message property, which is nil for silent notifications. For compatiblity, when filterSilentNotifications is set to false the
Compiles with Xcode 13.1 Batch requires Xcode 13 and iOS 10.0 or higher
Core
Updated BatchMessagingContentType's definition to use a modern NS_ENUM declaration. Fixes an issue where the header could not be parsed in an Objective-C++ context.
Messaging
Fixed a display issue on Modals with an image but no title.
Compiles with Xcode 13.1 Batch requires Xcode 13 and iOS 10.0 or higher
Core
Batch's XCFramework distribution now has an empty "PrivateHeaders" folder in the Mac Catalyst distribution to satisfy a broken link created by xcodebuild.
Inbox
Fixed an issue where the inbox wouldn't return notifications if the installation was upgraded from an application version running Batch 1.17. Fresh app installs worked as expected.
Compiles with Xcode 13.1 Batch requires Xcode 13 and iOS 10.0 or higher
Starting with this release, Batch can be installed as a XCFramework when using Carthage 0.38 and higher
Core
Added Universal Links support. If your application handles them, please declare your associated domains with [Batch setAssociatedDomains:];. If your site uses multiple subdomains (such as example.com, www.example.com, or support.example.com), each requires its own entry like in the Associated Domains Entitlement.
Events
Added NSURL type for the events data.
Batch now emits the BatchEventTrackerFinishedNotification NSNotification when the event tracker has finished sending events. This notification might be triggered multiple times in a row.
Read BatchTrackingEventsDidFinishWithSuccessKey in the userInfo to know whether the operation succeeded or failed.
Attributes
Added NSURL type for the custom attributes.
Inbox
Batch will now cache notifications marked as read or deleted to avoid any synchronization issues.
Property isDeleted from a BatchInboxNotificationContent is now deprecated: see BatchInbox.h for more info.
Fixed an issue where deleting a notification would not be reflected in allFetchedNotifications before re-fetching from the server.
Debug
Batch will now copy the installation ID to the clipboard when the application is foregrounded multiple times in a short timespan. This is enabled by default, you can disable it at any moment by using [Batch setEnablesFindMyInstallation:false].
Compiled with Xcode 12.4 Batch requires Xcode 12 and iOS 10.0 or higher
Core
Resolved an issue introduced in 1.17.2 where [Batch optOutWithCompletionHandler:] ignored the developer callback and behaved as if BatchOptOutNetworkErrorPolicyIgnore was returned.
Compiled with Xcode 12.4 Batch requires Xcode 12 and iOS 10.0 or higher
Core
Resolved an issue where calling [Batch optOut] or [Batch optOutAndWipeData] could crash if Batch wasn't started.
Stopped requesting the Identifier for Vendors (IDFV) from iOS. It was unused since we discontinued Batch Unlock in 2017.
IDFA isn't displayed in the debug view anymore, even if IDFA collection is enabled.
Inbox
Fixed an issue where using markAsRead would mark future notifications as read as soon as they arrived.
Fix an issue where fetchNewNotifications would behave like fecthNextPage if notifications were already loaded.
Compiled with Xcode 12.4 Batch requires Xcode 12 and iOS 10.0 or higher
Messaging
Fix a bug where a user could not interact with the application under a Banner format on iPadOS 14. iPhones were unaffected.
Fix a very rare race condition where the SDK could crash in -[BALocalCampaignsManager isEventWatched:].
Compiled with Xcode 12.4 Batch requires Xcode 12 and iOS 10.0 or higher
Core
Added the ability to enable Batch's internal logging using [Batch setInternalLogsEnabled:true]. They can also be enabled using the -BatchSDKEnableInternalLogs process argument.
Internal logs should be disabled when submitting your app to the App Store.
Added nullability annotations to BatchLoggerDelegate. This might trigger a warning in your Swift code.
Actions
Add a "copy to clipboard" builtin action (batch.clipboard).
Add a "rate app" builtin action (batch.rating). Requires iOS 10.3 or higher.
Messaging
Added support for a new UI format: WebView. See documentation for more info.
Added batchMessageWasCancelledByError: to BatchMessagingDelegate, called when a message could not be loaded due to an error when loading the message's content.
Added batchWebViewMessageDidTriggerAction:messageIdentifier:analyticsIdentifier: to BatchMessagingDelegate, called when a message could not be loaded due to an error when loading the message's content.
Event Dispatchers
Added the new event types BatchEventDispatcherTypeMessagingCloseError and BatchEventDispatcherTypeMessagingWebViewClick.
Push
Notification opens, deeplinks and mobile landings are now working when notifications aren't displayed in the foreground (willShowSystemForegroundAlert: false).
Inbox
[BatchInbox fetcherForUserIdentifier:authenticationKey:] now returns a nil instance if "userIdentifier" is nil.
Compiled with Xcode 12.4 Batch requires Xcode 12 and iOS 10.0 or higher
This release backports changes from 1.17.1
Messaging
Fix a bug where a user could not interact with the application under a Banner format on iPadOS 14. iPhones were unaffected.
Fix a very rare race condition where the SDK could crash in -[BALocalCampaignsManager isEventWatched:].
A migration guide from Batch 1.15 and lower is .
BREAKING: This version drops support for iOS 8 and 9 Batch requires Xcode 12 and iOS 10.0 or higher
Batch now depends on libz. This might require a change in your project:
Cocoapods: The podspec has been updated to add the required linker flag. No action is needed.
Carthage/Manual integration: When building, Xcode should automatically link your project with libz. If you get a compilation error, add -lz to your linker flags, or add libz to "Frameworks and Libraries" in your app's target.
Batch and Apple Silicon In order to support Apple Silicon, Batch will adopt the XCFramework format: it is not possible to support the iOS Simulator on Apple Silicon macs in a "fat" framework. What it means for you:
Cocoapods users will be migrated to the XCFramework automatically
Carthage users will stay on the old distribution style until Carthage supports XCFrameworks.
Manual integrations should update to XCFramework as soon as possible. Batch will be distributed in both formats for a couple of versions.
Note that the armv7s slice is not included in the XCFramework distribution.
BatchExtension
BatchExtension isn't distributed with the SDK zip anymore. It will be released on github soon after this release.
Core
Batch is now compatible with iOS 14's tracking consent and IDFA changes.
Added UIScene support. If your application uses it, you must add an UNUserNotificationCenterDelegate, otherwise Direct Opens, Deeplinks and Mobile Landings will not work: UIScene disables legacy push application delegate methods.
eSIMs are now supported. Phones that only have an eSIM will now properly report back their carrier, if the feature hasn't been disabled.
Event Dispatchers
BatchEventDispatcherTypeNotificationOpen is no longer broadcasted when the application is processing a background push.
Inbox
Enhanced the way the SDK fetchs notifications from the servers to greatly reduce bandwidth usage. No public API change.
Push
Add a new method BatchPush.isBatchPush to check if a received push comes from Batch.
Added BatchUNUserNotificationCenterDelegate, an UNUserNotificationCenterDelegate implementation that forwards events for Batch. Call BatchUNUserNotificationCenterDelegate.register() to automatically set it as your delegate. BatchUNUserNotificationCenterDelegate can display notifications when your app is in the foreground using the showForegroundNotifications property.
Messaging
The "modal" format now correctly triggers actions after it has been dismissed. This will have an impact on when the custom actions are executed, making them more consistent with the Fullscreen format.
This fixes an issue where deeplinks could not be opened in the application using SFSafariViewController with modal formats.
The image format is now properly sized when in a freeform window (iPad in Split View, Catalyst)
Fix a rare race condition with the interstitial format where it would fail to layout properly when the image server could not be reached.
User
Added new strongly typed methods for setting attributes on BatchUserDataEditor. They greatly enhance Swift usage of the API. See setBooleanAttribute:forKey:error and similar methods (set(attribute: Bool, forKey: String) in Swift).
Those new methods return validation errors: you can now know if your attribute key/value does not pass validation and will be discarded.
nil values are not supported in the new methods. Use removeAttributeForKey: explicitly.
Debug
Clicking the "share" buttons in the debug views no longer crash on iPads.
Compiled with Xcode 11.5 This minor release is the last one to support iOS 8 and 9.
Event Dispatcher
Fix an issue where event dispatchers might not be called.
User
Fix an issue where events that had not been sent to the server would be lost when the app's process was killed.
Compiled with Xcode 11.5
User
Added support for Date in BatchEventData.
BatchEventData now supports up to 15 attributes (from 10).
Compiled with Xcode 11.3.1
This is the LAST release that support iOS 8 and 9. Future releases will require iOS 10+
Core
Changed how notification status is reported: The SDK will now tell Batch's servers that notifications are enabled if :
The app has requested and holds a provisional authorization.
The user has disabled banners but kept lockscreen or notification center delivery enabled.
Added support for external analytics using BatchEventDispatcher. See the documentation for more details.
Messaging
New enum property BatchMessagingContentType contentType on class BatchInAppMessage to help cast to the right content class.
A new optional delegate method BatchMessagingDelegate.presentingViewControllerForBatchUI allows to specify which view controller to display Batch messages on.
Fixed an issue where the last line of a label could be cut.
Inbox
Added the markNotificationAsDeleted: method on BatchInboxFetcher, allowing the deletion of notifications
This release has been built using Xcode 11.1.
Messaging
Fix an issue where mobile landings might fail to be shown after opening a push for a backgrounded app on iOS 13
Fix an issue where BatchDeeplinkDelegate does not fire in certain conditions when opening a push from the lockscreen
Fix an issue where banners would move more than expected when swiping on them on iOS 13
This release officially supports iOS 13. It has been built using and requires Xcode 11.0 GM 2.
This is the LAST release that support iOS 8 and 9. Apple has already removed their simulators from Xcode 11.
Messaging
Add UIScene support
Add an analytics event for iOS 13's modal swipe-to-dismiss
Core
Bug fix: deeplinks from actions are properly sent to Deeplink delegate method
Fixed an issue where the server-side GDPR wipe event was not sent properly
Fixed an issue where the Installation ID may not be wiped properly after a GDPR wipe
User
High level data (language/region/custom user id) can now be read back.
User data (attributes and tags) can now be read back.
Messaging
Added support for two new UI formats: Modal, and Image. See the documentation for more information.
Added support for GIFs in Mobile Landings and In-App messages
Added support for rich text.
Added support for text scrolling in all formats. Banners will now have a maximum body height of ~160pt, and their text will scroll.
Push
BatchPushAlertDidResignNotification is sent when user dismissed the Remote notification authorization alert. Notification's userInfo dict contains user's choice in BatchPushNotificationDidAcceptKey
Core
Fixed a rare race condition crash that could happen when tracking an event while In-App campaigns are fetched from the server.
User
Fixed an issue where adding tags would not work with 1.13.1
Re-release of 1.13.0, compiled with Xcode 10.1. Batch now includes an arm64e slice. arm64e bitcode isn't present
Note: This release comes with an update to the included BatchExtension framework. Its strip-frameworks script will strip the arm64e slice to prevent bitcode-related submission errors: we strongly discourage from enabling arm64e in your extension's Architectures
Core
Fixed a rare crash that could happen when Batch's internal database failed to initialize in [BAUserDataManager startAttributesSendWSWithDelay:].
Opting-out from the SDK now sends an event notifying the server of this. If a data wipe has been asked, the request will also be forwarded to the server. New methods have been introduced to be informed of the status of the request to update your UI accordingly, and possibly revert the opt-out if the network is unreachable.
Added the BatchDeeplinkDelegate protocol. Adopting it allows you to manually process deeplink open requests from Batch, rather than having to implement openURL
Push
The SDK will report whether notifications are allowed, denied, or undecided more accurately on iOS 10 or higher
Added a method to easily open iOS' settings for the current application's notification settings
Split +[BatchPush registerForRemoteNotifications] into more explicit methods.
Events
Event data support has been overhauled. As a result:
Introduced BatchEventData. Use this class to attach attributes and tags to an event. See this class' documentation for more information about limits.
+[BatchUser trackEvent:withLabel:data:] has been deprecated
Calls to this method will log deprecation warnings in the console
Added methods to handle opting-out from Batch, and wiping user data. Please see [Batch optOut], [Batch optOutAndWipeData] and [Batch optIn]. You can control whether Batch is opted out from by default setting a boolean TRUE in your Info.plist for the key:
BATCH_OPTED_OUT_BY_DEFAULT For compatibility reasons, Batch will be enabled by default. More info in our documentation.
Fixed a bug where an In-App Campaign might not trigger for a certain configuration if a tracked event had a label
Added support for Banners in In-App Messaging
If your app uses the Mobile Landing/In-App Messaging manual mode, and especially [BatchMessaging loadViewControllerForMessage:error:] you need to change the way you present the view controllers manually: please check the for more information. A helper has been added on BatchMessaging, which is especially useful if you don't feel like making your own UIWindow: [BatchMessaging presentMessagingViewController:]
Fix In-App Campaigns not honoring the grace period
Fix an issue where on iOS 11, the fullscreen Messaging template may not be displayed correctly for themes with no CTA
Added [BatchMessaging presentViewController:], which takes care of showing a previously loaded BatchMessagingViewController in the most appropriate way.
If you used [BatchMessaging loadViewControllerForMessage:error:] to display messages in manual mode, you should go to the messaging documentation to follow updated instructions: failure to do so will result in incorrect banner behaviour.
Added support for Smart Invert in Mobile Landings/In-App Messaging. Images won't be inverted anymore.
Added iPhone X support for Mobile Landings/In-App Messaging.
Added a method on BatchInAppMessage, allowing you to fetch the visual content of the message. See BatchInAppMessage's content property for more info
Fixed a bug where overriding a bold font would not work proper for mobile landings
Various bugfixes
Removed the one minute minimum delay between two In-App campaign triggers
Fixed a bug where In-App Campaigns were sometimes not refreshed when coming back in foreground
Fixed a bug where In-App Campaigns failed to trigger on "Next Session"
Other bugfixes
Fixed issues found by the Main Thread Checker in Xcode 9
Introduced In-App Campaigns
Added a Do Not Disturb mode on BatchMessaging, allowing easier control of when landings will be shown
Added the Inbox module, allowing you to fetch previously received notifications from your code. More info: https://batch.com/doc/ios/inbox.html
BREAKING CHANGE: Removed BatchAds and BatchUnlock methods and related classes.
Added BatchUser.trackLocation, allowing you to natively track user position updates
Deprecated Batch.isRunningInDevelopmentMode. It is useless, as DEV API Keys always start with "DEV"
Fix a temporary freeze when opening a push containing a deeplink when triggered by a UNUserNotification delegate
Fix a bug where the delegate set on BatchMessaging was never called.
A bug where landings and open rate tracking didn't work when coming from a cold start in some situations on iOS 10.1/10.2 was fixed. Apps implementing UNUserNotificationCenterDelegate or the "fetchCompletionHandler:" variant of the legacy app delegate callbacks are not concerned.
Applying changes with the user data before starting Batch doesn't fail silently anymore, but now logs an error.
Improved log clarity for some errors
Rebuild the SDK without debug symbols
Fix the handling of notifications opened while the app is on the screen when using UNUserNotificationCenter on iOS 10. The "bug" remains on iOS 9 because of framework limitations.
Introduced
Batch now requires iOS 8 or greater due to Xcode 8 dropping support for earlier versions.
[Batch setUseAdvancedDeviceInformation:] has been introduced to reduce the quantity of device information Batch will use. Note that disabling this will limit several dashboard features.
Batch will now use the UserNotification framework when possible. UIUserNotificationCategory instances given to [BatchPush setNotificationsCategories:] are automatically converted to UNNotificationCategory on iOS 10.
BatchPush has new methods that allows it to be integrated into a UNUserNotificationCenterDelegate instance.
Since iOS 10 changes quite a lot in how notifications work, it is strongly advised that you read our iOS 10 documentation: https://batch.com/doc/ios/advanced/ios10-migration.html . Upgrading to UNUserNotificationDelegate is recommended.
Fixed push token environment detection (Production/Sandbox) logging
Fixes a bug where +[BatchPush trackEvent:withLabel:] didn't track the event label correctly
Removed most of the categories used by the SDK. -ObjC shouldn't be needed anymore, but it is encouraged to keep it
Added support for manual integrations: Application Delegate swizzling can now be disabled, but ALL Batch entry points should be implemented carefully. More info on https://batch.com/doc
Improved support for notifications containing "content-available":1
Added .gitignore in "Batch.bundle" to fix git issues for those who commited Batch itself
Minor bugfixes
Fixed: push token environment detection failed in some rare cases
Custom user data (attributes, tags and events)
Added an API to retrieve Batch's unique installation identifier
Deprecated BatchUserProfile
HTTP/2 Support (NSURLSession replaces the NSURLConnection backend when available)
Deprecated Batch Ads
Added a method for getting the last known push token
Improved push registration logs in case of failure
Fixed Xcode 7 compatibility when integrated via CocoaPods NOTE : If you previously integrated Batch, you may need to remove lines that look like "$(PODS_ROOT)/Batch/**" from "Framework Search Paths" in your Build settings for Batch to work with Xcode 7.
Improved Native Ads:
Added properties on BatchNativeAd to get image paths on disk (icon and cover)
Added a method to load Native Ads without preloading their UIImages
Added a MoPub custom event for Native Ads: BatchMoPubCustomNative
Fix an issue that could cause a freeze if your application embedded custom fonts and was started in Airplane mode
Native Ads
Renamed ad methods to avoid confusion between interstitial ads and native ads. Old methods are deprecated but still work
Faster interstitial ad display
"Batch" Objective-C module now imports everything. Base Batch functionality is in the new "Batch.Core" submodule.
iOS 6 Bugfixes
Ads optimisations
Bug fixes
Bug fix
Better push implementation
Deeplink management improvement
Bug fix
Batch Ads
Stability improvements
Add push capabilities.
iOS 8 and Objective-C modules support.
Dropped iOS 5 compatibility.
BatchUnlockDelegate is now weakly retained, rather than strongly retained.
Move a file put by mistake in /Documents/ into /Library/Application Support/
Batch release.
Fixed incorrect internal analytics events
batchMessageWasCancelledByUserActionbatchMessageWasCancelledByErrorbatchMessageDidDisappear:(NSString *_Nullable)messageIdentifier reason:(BatchMessagingCloseReason)reasonThe batchMessageDidTriggerAction delegate method now provides a ctaIdentifier (NSString) instead of the previous actionIndex (NSInteger). BatchMessageGlobalActionIndex's type has been changed accordingly. This method is still called on MEP (Mobile Engagement Platform) messages, but you will now receive an identifier built from the old cta index : “mepCtaIndex:” + index.
Renamed property content from class BatchInAppMessage to mepContent. This method will return nil and contentType property will return BatchMessagingContentTypeUnknown for messages coming from the CEP (Customer Engagement Platform).
Fixed an issue where dismiss action didn't trigger batchMessageDidTriggerAction
Fixed a rare issue where Batch could presents a message on a UIAlertController
identify API to avoid unintended values.Removed method setLoggerDelegate, which has been replaced by the loggerDelegate property.
Removed method setAssociatedDomains, which has been replaced by the associatedDomains property.
Removed method isOptedOut, which has been replaced by the isOptedOut property.
Removed method isRunningInDevelopmentMode with no equivalent.
Removed method handleURL with no equivalent.
Removed method setUseAdvancedDeviceInformation, you should now use the added updateAutomaticDataCollectionAPI.
Renamed method debugViewController to makeDebugViewController.
Removed warning about unconfigured App Groups.
Added setDisabledMigrations(migrations:)to explicitly disable profile's data migrations when running the SDK V2 for the first time when your app belongs to a project.
Added updateAutomaticDataCollection(editor:) to fine-tune the data sent by the SDK since Batch will not resolve the user's location/region and will not send the device type by default.
Removed deprecated method registerForRemoteNotificationsWithCategories.
Removed method setNotificationsCategories.
Removed deprecated method handleNotification.
Removed method handleNotification:actionIdentifier.
Removed method enableAutomaticDeeplinkHandling, which has been replaced by the enableAutomaticDeeplinkHandling property.
Removed method lastKnownPushToken, which has been replaced by the lastKnownPushToken property.
Added async variants of BatchPush.requestNotificationAuthorization() and requestProvisionalNotificationAuthorization(). In non async Swift code and Objective-C, they are exposed as variants with a completion handler: BatchPush.requestNotificationAuthorization(completionHandler:)/[BatchPush requestNotificationAuthorizationWithCompletionHandler:]. Note: In async Swift code, this is a breaking change as the language will force you to use the async variant. If you want to ignore the result, use: let _ = try? await BatchPush.requestNotificationAuthorization().
Removed pre-iOS 10 UINotification related methods. UNUserNotification and its related callbacks have been the only supported way to integrate Batch for a while as legacy methods lead to unexpected behaviour.
Removed deprecated method handleRegisterUserNotificationSettings.
Added helpers to extract Batch notification information from UNNotification instances.
Added method isBatchNotification
Added method deeplinkFromNotification
BatchUNUserNotificationCenterDelegate now defaults to showing foreground notifications.
BatchProfile.editorBatchProfileEditorBatchProfile.editWithBlockRemoved method printDebugInformation with no equivalent.
Removed methods trackTransactionWithAmount and trackTransactionWithAmount:data with no equivalents.
Removed methods trackLocation, you should now use BatchProfile.trackLocation.
Added BatchUser.clearInstallationData() which allows you to remove the installation data without modifying the current profile.
Removed addTag API from BatchEventData You should now use the $tags reserved key with putStringArray:forKey: method.
Removed parameter label from trackEvent APIs. You should now use the $label reserved key in BatchEventAttributes with the putStringt:forKey: method.
Added support for Array and Object attributes with the added APIs to BatchEventAttributes :
putObject:forKey
putObjectArray:forKey
putStringArray:forKey
Added validateWithError method to BatchEventAttributes to ensure your event attributes are valid before sending them.
automaticModeRemoved deprecated property isDeleted from BatchInboxNotificationContent.
editWithBlockAdded trackEventWithName and trackEventWithName:attributes APIs as replacement of the BatchUser.trackEvent methods.
Added trackLocation API as replacement of the BatchUser.trackLocation method.
Added code signature to the Batch's XCFramework distribution.
displayLandingMessage()bodyAdded the isSilent property on BatchInboxNotificationContent to identify silent notifications.
Modal formats can now be closed with the escape key
Fix a bug where cached in-app campaigns might not be deleted when all campaigns were disabled
In-App campaign cache is now cleared on server errors
Fix an issue where the statusbar color would not be applied
Support for TLS versions older than 1.2 has been removed.
Added a new builtin action named batch.ios_tracking_consent, which requests tracking consent via AppTrackingTransparency. More info in the documentation.
UNUserNotificationCenterDelegate by the time application:didFinishLaunchingWithOptions: returns. Implementing this delegate improves Batch's handling of various features that rely on notification interaction feedback, such as analytics or deeplinks: it is strongly recommended that you implement it if you don't already.Batch now emits the BatchPushOpenedNotification NSNotification when a notification has been opened by the user. This deprecates BatchPushReceivedNotification: see BatchPush.h for more info.
In automatic mode, application:didReceiveRemoteNotification:fetchCompletionHandler:'s completion handler is no longer called on your behalf when a deeplink is opened.
Modally presented messages will not be allowed to be dismissed unless they're the frontmost view controller. This fix an issue where a message with an autodismiss might dismiss a view controller presented on top of it.
Improved dismissal logic. While automatic dismissal may fail in some rare occasions due to iOS refusing to dismiss a modal view controller when one is animating, it will not prevent the user from manually dismissing the view anymore.
Improved accessibility of all message formats
Deeplinks can now be open directly in the app using a SFSafariViewController for Push Notifications, Mobile Landings and In-App Mesasges
Added new methods on the messaging delegate allowing you to track more information such as close/autoclose and button presses. More info in the Mobile Landings documentation.
In swift, BatchMessaging.setAutomaticMode has been renamed to BatchMessaging.setAutomaticMode(on:)
Batch.deeplinkDelegate+[BatchPush requestNotificationAuthorization] shows the system notification authorization prompt, and then fetches the push token. Equivalent to calling +[BatchPush registerForRemoteNotifications].+[BatchPush refreshToken] will only ask iOS for a new token. This needs to be called on every application start to handle upgrades from versions without Batch, or if iOS changes the push token.
Added support for iOS 12's notification changes:
You can now ask for provisional notification authorization using +[BatchPush requestProvisionalNotificationAuthorization]. This method does nothing on versions lower than iOS 12.
You can now ask Batch to tell iOS that your app supports opening an in-app notification settings from the system settings by calling [BatchPush setsetSupportsAppNotificationSettings:true] Note that this still requires you to implement a UNUserNotificationCenterDelegate, and the appropriate method to open the settings.
BatchEventData. Same data format restrictions apply: Any key/value entry that can't be converted will be ignored, and logged. Tags are not supportedIntroduced +[BatchUser trackEvent:withLabel:associatedData:] which uses BatchEventData, replacing the deprecated method.
Swift users: Since Swift allows methods to be overloaded by type, the method and arguments name do not change: simply replace your data dictionary with a BatchEventData instance Example: BatchUser.trackEvent("event_name", withLabel: "label", data: BatchEventData())
The SDK will now log the current Installation ID on start
Delay button handling in messaging views to until animations are over. This may have prevented deeplinks or custom actions from working properly, as UIKit does not support presenting a view controller while one is already animating.
Fix In-App Campaigns accidentally performing too much work on the main thread.
Fix a concurrency issue with In-App Campaigns which could very rarely cause a crash
Fixed a memory leak with network requests
Rewrote documentation with a more modern syntax. Our API docs are now based on jazzy.
[BatchPush registerForRemoteNotificationsWithCategories:] has been split in two methods and is now deprecated
Introduced a new dynamic framework, BatchExtension. It is compatible with application extensions APIs, and will progressively bring more Batch features to your extensions. In this version, it is used to add support for Rich Notifications.
Deprecated [BatchPush setupPush]. It is now implied.
Added Bitcode support
Automatic deeplink handling can now be disabled
Added a method for getting the deeplink from Batch Push
Batch now logs starts for DEV API Keys
Batch now requires you to link with libsqlite3.dylib