-
Notifications
You must be signed in to change notification settings - Fork 28
New Architecture and Expo framework
- Installation
- React Native API Interface
Important
New Archtecture and Expo framework are in Pilot release. Please contact your Client Success Manager before starting the implementation.
The React Native wrapper for SAP Emarsys SDK automatically integrates the Emarsys SDK into your Expo app’s native modules.
- Add the plugin config options to your
app.jsonwith your own values:
{
"expo": {
...
"plugins": [
...
[
"react-native-emarsys-sdk",
{
"applicationCode": <APPLICATION_CODE: STRING>,
"merchantId": <MERCHANT_ID: STRING>,
"enableConsoleLogging": <ENABLE_CONSOLE_LOGGING: BOOL>,
"androidSharedPackageNames": <ANDROID_SHARED_PACKAGE_NAMES: LIST>,
"androidSharedSecret": <ANDROID_SHARED_SECRET: STRING>,
"iosSharedKeychainAccessGroup": <IOS_SHARED_KEYCHAIN_ACCESS_GROUP: STRING>
}
]
]
...
}
}-
Add your
google-services.jsonfile into the app’s assets folder. -
(Optional) Provide a custom Android push notification icon:
- Place an image named
mobile_engage_logo_icon.jpginside the app’sassetsfolder. - During build, it will be copied into the correct Android resources directory (
res/drawable).
- Place an image named
-
Run prebuild to apply the changes:
npx expo prebuildEmarsys SDK setup should be implemented natively to ensure setup method is the first thing to be called.
⚠️ If you are using RN version 0.62 or higher: Make sure to place Emarsys SDK imports outside#ifdef FB_SONARKIT_ENABLEDcondition!
The SDK initialisation should be done in didFinishLaunchingWithOptions in AppDelegate.
objective-c
#import <EmarsysSDK/Emarsys.h>
#import <EmarsysSDK/EMSConfig.h>
#import <RNEmarsysSDK/RNEmarsys.h>- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//...
EMSConfig *config = [EMSConfig makeWithBuilder:^(EMSConfigBuilder * builder) {
[builder setMobileEngageApplicationCode:@<APPLICATION_CODE: STRING>]; // your application code
[builder setMerchantId:@<MERCHANT_ID: STRING>]; // your predict merchant ID
[builder enableConsoleLogLevels:<ENABLE_CONSOLE_LOG_LEVELS: ARRAY>];
[builder setSharedKeychainAccessGroup:@<IOS_SHARED_KEYCHAIN_ACCESS_GROUP: STRING>];
}];
[Emarsys setupWithConfig:config];
UNUserNotificationCenter.currentNotificationCenter.delegate = [Emarsys push];
[RNEmarsys setup];
//...
return YES;
}swift
import EmarsysSDK
import RNEmarsysSDKfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//...
let config = EMSConfig.make { (build) in
build.setMobileEngageApplicationCode(<APPLICATION_CODE: String>)
build.setMerchantId(<MERCHANT_ID: String>)
build.enableConsoleLogLevels(<ENABLE_CONSOLE_LOG_LEVELS: Array>)
build.setSharedKeychainAccessGroup(<IOS_SHARED_KEYCHAIN_ACCESS_GROUP: String>)
}
Emarsys.setup(config: config)
UNUserNotificationCenter.current().delegate = Emarsys.push
RNEmarsys.setup()
//...
return true
}Follow Google's instructions to add the Google Play Services gradle plugin to your project: https://developers.google.com/android/guides/google-services-plugin
In order for push notifications to work, you need to obtain Firebase Cloud Messaging credentials for your app. Follow the instruction for the native SDK here: https://github.com/emartech/android-emarsys-sdk/wiki/Obtaining-Firebase-Cloud-Messaging-credentials, then copy the google-services.json file to the android/app directory of your React Native project.
When the push token arrives from Firebase, we need to set it using Emarsys.push.setPushToken(). The recommended way of using the Android SDK is to enable the SDK to automatically handle setPushToken and trackMessageOpen calls for you, please register the service in your android/app/AndroidManifest.xml file.
<service android:name="com.emarsys.service.EmarsysFirebaseMessagingService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>Additionally, you can set a custom notification icon, by specifying it as a meta-data in your application tag:
<meta-data
android:name="com.emarsys.mobileengage.small_notification_icon"
android:resource="@drawable/notification_icon"
>The SDK has to be initialised in the onCreate method of the MainApplication.
java
import com.emarsys.Emarsys;
import com.emarsys.config.EmarsysConfig;
import com.emarsys.reactnative.RNEmarsys;public void onCreate() {
super.onCreate();
//...
EmarsysConfig config = new EmarsysConfig.Builder()
.application(this)
.applicationCode(<APPLICATION_CODE: STRING>)
.merchantId(<MERCHANT_ID: STRING>)
.enableVerboseConsoleLogging()
.sharedPackageNames(<SHARED_PACKAGE_NAMES: LIST>)
.sharedSecret(<SHARED_SECRET: STRING>)
.build();
Emarsys.setup(config);
RNEmarsys.setup()
createNotificationChannels();
//...
}kotlin
import com.emarsys.Emarsys
import com.emarsys.config.EmarsysConfig
import com.emarsys.reactnative.RNEmarsysoverride fun onCreate() {
super.onCreate()
//...
val config = EmarsysConfig(
application = this,
applicationCode = <APPLICATION_CODE: STRING>,
merchantId = <MERCHANT_ID: STRING>,
verboseConsoleLoggingEnabled = <VERBOSE_CONSOLE_LOGGING_ENABLED: BOOLEAN>,
sharedPackageNames = <SHARED_PACKAGE_NAMES: LIST>,
sharedSecret = <SHARED_SECRET: STRING>)
Emarsys.setup(config)
RNEmarsys.setup()
createNotificationChannels()
//...
}The Emarsys SDK automatically handles setPushToken for the device and it is recommended to leave this to the SDK. However if you have your custom implementation of MessagingService, please use the setPushToken() method, to set the push token.
For Android 8.0 (API level 26) and higher, you need to create notification channels. Please refer to the Android Oreo Channels documentation for detailed instructions.
The push token has to be set natively when it arrives in didRegisterForRemoteNotificationsWithDeviceToken in the AppDelegate:
objective-c
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[Emarsys.push setPushToken:deviceToken];
}swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Emarsys.push.setPushToken(deviceToken)
}Push notification could show media content and action buttons besides the title and body. Push notifications with these types of contents are called Rich Notifications.
-
Add a new Notification Service Extension target to your project.
-
Add the
EmarsysNotificationServiceto this target in thePodfile.
target 'EMSNotificationService' do
pod 'EmarsysNotificationService'
end
-
Install pods with the
pod installcommand in the iOS workspace directory via terminal. -
Open the
NotificationService.hin the target, then:
- Import the <EmarsysNotificationService/EMSNotificationService.h>.
- Extend the class EMSNotificationService instead of UNNotificationServiceExtension.
- Remove all default implementation in the class.
objective-c
#import <EmarsysNotificationService/EMSNotificationService.h>
@interface NotificationService : EMSNotificationService
@endswift
import EmarsysNotificationService
class NotificationService: EMSNotificationService {
}
NoteThe
NotificationServiceclass should remain empty. All functionality is handled by theEMSNotificationServicebase class.
Silent messages arrives in application:didReceivedRemoteNotification:fetchCompletionHandler:, so in order to be able to handle them, call handleMessageWithUserInfo: method there
objective-c
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[Emarsys.push handleMessageWithUserInfo:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}swift
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
Emarsys.push.handleMessage(userInfo: userInfo)
completionHandler(.newData)
}For the location permissions the applications Info.plist must be extended with the following keys:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>AlwaysUsage is a must have for region monitoring (or some description of your choice)</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>AlwaysUsage is a must have for region monitoring (or some description of your choice)</string>Make sure that your app is requesting the required permissions from the user. To make it easier, you can call our requestAlwaysAuthorization method.
For the location permissions the applications AndroidManifest.xml must be extended with the following permissions:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />Make sure that your app is requesting the required permissions from the user. From Android 12, the ACCESS_FINE_LOCATION also needs the ACCESS_COARSE_LOCATION permission, since the user can now prevent applications from accessing the precise location of the phone. In response to this, both android.permission.ACCESS_COARSE_LOCATION and android.permission.ACCESS_FINE_LOCATION permissions are mandatory for geofences to work.
From Android 12, when the ACCESS_FINE_LOCATION permission is granted to the Application, the geofencing will work as before. If only ACCESS_COARSE_LOCATION is granted, then we can't guarantee that the geofences will trigger at the correct times.
Important Note:Geofencing is disabled on devices that do not have Google Play Services!
import Emarsys from 'react-native-emarsys-sdk';The contactFieldId should not be set to 3 (email). In order to prevent your customers' personal data (PII) being stored in our cloud infrastructure, we require use of unique, non-guessable and immutable contact identifiers. Customer ID's are considered secure. Salted email hash is no longer supported for new Mobile Engage implementations. To make sure the behavioural tracking is cross devices (i.e. mobile and web), the Web Extend contact ID should also use Customer ID and match that used on mobile.
After the application setup is finished, you can use setContact method to identify the user with contactFieldId and contactFieldValue. Without setContact all events will be tracked as anonymous usage.
const contactFieldId = 987654321;
const contactFieldValue = '123456789';
await Emarsys.setContact(contactFieldId, contactFieldValue);When the user signs out, we should use the clearContact method. The method is going to automatically log in an anonymous user instead of the one leaving.
NoteNo need to call
clearContactevery time, even if the user isn't logged in. Just make sure it is called when the user logs out of the application.
await Emarsys.clearContact();If you want to track custom events, the trackCustomEvent method should be used, where the eventName parameter is required, but the eventAttributes are optional.
const eventName = 'test-event';
const eventAttributes = { k1: 'v1', k2: 'v2' };
await Emarsys.trackCustomEvent(eventName, eventAttributes);Add setEventHandler to the useEffect in your App.js.
setEventHandler returns React Native EventSubscription, which could be unsubscribed (remove()) when needed, e.g. in the cleanup function.
import { type Event } from 'react-native-emarsys-sdk';
const eventHandlerSubscription = useRef<EventSubscription | null>(null);
useEffect(() => {
eventHandlerSubscription.current = Emarsys.setEventHandler((event: Event) => {
Alert('Event', `${event.name}: ${JSON.stringify(event.payload)}`);
});
return () => {
eventHandlerSubscription.current?.remove();
eventHandlerSubscription.current = null;
}
}, []);The push token is automatically handled by the native integration. No additional implementation is required.
If needed, you can manually update the token calling setPushToken():
const pushToken = '1234567890'; // Should retrieve the actual push token here
await Emarsys.push.setPushToken(pushToken);Caution
If your app is already live and you add the Emarsys SDK, existing installations will not be opted in automatically.
You must retrieve the existing push tokens and set them manually:
- Android: via Firebase Cloud Messaging
- iOS: via APNs registration
If you want to remove the push token for the contact, please use clearPushToken() method.
await Emarsys.push.clearPushToken();If you want to get the push token for the contact, please use getPushToken() method.
const pushToken = await Emarsys.push.getPushToken();When a critical activity starts and should not be interrupted by in-app, use pause() to pause in-app messages.
await Emarsys.inApp.pause();In order to show in-app messages after being paused, use the resume() method.
await Emarsys.inApp.resume();In-App message, that takes place in the application's view hierarchy. Multiple inline in-app components are allowed in one screen.
Create the view with component InlineInAppView.
import { InlineInAppView } from 'react-native-emarsys-sdk';
const inlineInAppView = useRef<any>(null);
const [inlineInAppViewHeight, setInlineInAppViewHeight] = useState(0);
<InlineInAppView
ref={inlineInAppView}
style={{ width: '100%', height: inlineInAppViewHeight }}
onEvent={(event) => {
Alert(event.nativeEvent.name, JSON.stringify(event.nativeEvent.payload))
}}
onCompletion={(event) => {
if (!event.nativeEvent.error) {
setInlineInAppViewHeight(125);
} else {
console.log(event.nativeEvent.error)
}
}}
onClose={() => {
setInlineInAppViewHeight(0);
}}
/>In order to load the inline in-app, loadInApp(<ViewRef>, <String>) must be called with the corresponding viewId.
Emarsys.inApp.loadInlineInApp(inlineInAppView.current, viewId);We won't go into the details to introduce how Predict works, and what its capabilities are, rather we aim to explain the mapping between the Predict commands and our interface. Please visit Predict's documentation for more information.
To use the Predict functionality you have to setup your merchantId during the initialization of the SDK. In order to track Predict events, you can use the methods available on our Predict interface.
When you want to track the cart items in the basket, you can call the trackCart() method with a list of CartItem. CartItem is an interface that can be used in your application for your own CartItem and then simply use the same items with the SDK.
import { type CartItem } from 'react-native-emarsys-sdk';
const items: CartItem[] = [
{ itemId: 'item1', price: 1.1, quantity: 1 },
{ itemId: 'item2', price: 2.2, quantity: 2 }
];
await Emarsys.predict.trackCart(items);When you want to track empty basket.
const items: CartItem[] = [];
await Emarsys.predict.trackCart(items)To report a purchase event, you should call trackPurchase() with the items purchased and with an orderId.
const orderId = 'order1';
const items: CartItem[] = [
{ itemId: 'item1', price: 1.1, quantity: 1 },
{ itemId: 'item2', price: 2.2, quantity: 2 }
];
await Emarsys.predict.trackPurchase(orderId, items);To report a purchase event with empty basket.
const orderId = 'order2';
const items: CartItem[] = [];
await Emarsys.predict.trackPurchase(orderId, items);If an item was viewed, use the trackItemView() method with an itemId as required parameter.
const itemId = 'item1';
await Emarsys.predict.trackItemView(itemId);When the user navigates between the categories, you should call trackCategoryView() in every navigation. Be aware to send categoryPath in the required format. Please visit Predict's documentation for more information.
const categoryPath = 'Shoes>Pump';
await Emarsys.predict.trackCategoryView(categoryPath);To report search terms entered by the contact, use trackSearchTerm() method.
const searchTerm = 'searchTerm';
await Emarsys.predict.trackSearchTerm(searchTerm);To track custom tags, use the trackTag() method, where, the eventName parameter is required, but the tagAttributes is optional.
const tag = 'tag1';
const attributes = { k1: 'v1', k2: 'v2' };
await Emarsys.predict.trackTag(tag, attributes);With the Emarsys SDK you can ask for product recommendations based on different recommendation parameters.
import { Logic, Filter, type Product } from 'react-native-emarsys-sdk';
const logic = Logic.home(['1', '2']);
const filters = [Filter.exclude.isValue('field', 'value')];
const limit = 5;
const availabilityZone = 'en';
const recommendedProducts: Product[] = await Emarsys.predict.recommendProducts(logic, filters, limit, availabilityZone);
NoteFor more information of the recommender logics, please visit documentation.
The currently supported logics are:
Search logic - based on searchTerm
const searchTerm = 'searchTerm';
const logic = Logic.search(searchTerm);Cart logic - based on cartItems
const items: CartItem[] = [
{ itemId: 'item1', price: 1.1, quantity: 1 },
{ itemId: 'item2', price: 2.2, quantity: 2 }
];
const logic = Logic.cart(items);Related logic - based on itemId
const itemId = 'item1';
const logic = Logic.related(itemId);Category logic - based on categoryPath
const categoryPath = 'Shoes>Pump';
const logic = Logic.category(categoryPath);Also bought logic - based on itemId
const itemId = 'item1';
const logic = Logic.alsoBought(itemId);Popular logic - based on categoryPath
const categoryPath = 'Shoes>Pump';
const logic = Logic.popular(categoryPath);Personal logic - based on current browsing and activity
const variants = ['1', '2'];
const logic = Logic.personal(variants);Home logic - based on most recent browsing behaviour
const variants = ['1', '2'];
const logic = Logic.home(variants);The currently supported filters are:
const filters = [
Filter.exclude.isValue('field', 'value')
Filter.include.inValues('field', ['value1', 'value2']),
Filter.include.hasValue('field', 'value'),
Filter.include.overlapsValues('field', ['value1', 'value2']),
Filter.exclude.isValue('field', 'value'),
Filter.exclude.inValues('field', ['value1', 'value2']),
Filter.exclude.hasValue('field', 'value'),
Filter.exclude.overlapsValues('field', ['value1', 'value2'])
]The Emarsys SDK doesn't track automatically recommendationClicks, so you have to call manually trackRecommendationClick() when an interaction happens with any of the recommended products.
const product = recommendedProducts[i];
await Emarsys.predict.trackRecommendationClick(product);In order to track email link clicks that open the application directly with the Emarsys SDK, you need to call trackDeepLink.
Emarsys.trackDeepLink(url);Emarsys SDK provides a solution for applicationCode change in a convenient way, without restarting the SDK.
const applicationCode = 'applicationCode';
await Emarsys.config.changeApplicationCode(applicationCode);Emarsys SDK provides a solution for merchantId change in a convenient way, without restarting the SDK.
const merchantId = 'merchantId';
await Emarsys.config.changeMerchantId(merchantId);Provides what is the actual applicationCode set in the SDK.
const applicationCode = await Emarsys.config.getApplicationCode();Provides what is the actual merchantId set in the SDK.
const merchantId = await Emarsys.config.getMerchantId();Provides what is the actual contactFieldId set in the SDK.
const contactFieldId = await Emarsys.config.getContactFieldId();Provides what is the actual clientId set in the SDK.
const clientId = await Emarsys.config.getClientId();Provides what is the actual language code set in the SDK.
const languageCode = await Emarsys.config.getLanguageCode();Provides what is the actual sdk version in the SDK.
const SDKVersion = await Emarsys.config.getSdkVersion();Provides what is the actual RN wrapper version.
const RNWrapperVersion = await Emarsys.config.getRNWrapperVersion();In order to receive the message Inbox content, you can use the fetchMessages() method.
import { type Message } from 'react-native-emarsys-sdk';
const inboxMessages: Message[] = await Emarsys.inbox.fetchMessages();Tags are to be used to set the status of the inbox message, e.g. opened, seen etc. There are 6 tags in total and the details are confirmed in the table below. App developers can add the tags seen, opened, pinned and deleted. It is important to note all the tags though as they will be included in the message payload in the SDK tag field. Depending on the tag included in the message, the message could be handled differently by the app. An example would be that messages tagged with high (for High Priority) could be visible flagged/highlighted by the contact.
Warning
Only the tags specified below are supported. Any custom tags created will not work.
| Tag Name | Tag | Description | Where it can be added |
|---|---|---|---|
| High Priority | high | Marketer setting of high priority message. You can activate this tag from your inbox-campaign by ticking the relevant checkbox. Your developers have to graphically represent this setting in the app inbox. | SAP Engagement Cloud system |
| Cancelled | cancelled | Marketer cancelled the message. This tag is automatically added to your inbox campaign when you recall it. | SAP Engagement Cloud system |
| Seen | seen | Inbox messages visible in the app inbox but not opened | Mobile App via the SDK |
| Opened | opened | Messages opened on the app | Mobile App via the SDK |
| Pinned | pinned | Messages that are pinned as favourite by the contact | Mobile App via the SDK |
| Deleted | deleted | Messages that are deleted in the app's user interface | Both SAP Engagement Cloud system and/or Mobile App via the SDK |
import { Tag } from 'react-native-emarsys-sdk';To label a message with a tag, you can use addTag() method.
const tag = Tag.seen;
const messageId = inboxMessages[i].id;
await Emarsys.inbox.addTag(tag, messageId);To remove a label from a message, you can use removeTag() method.
const tag = Tag.seen;
const messageId = inboxMessages[i].id;
await Emarsys.inbox.removeTag(tag, messageId);Geofence makes it available to trigger certain actions based on the users location. When the user enters a predefined region (represented by latitude, longitude and radius) EmarsysSDK fires a custom event which can trigger an action, for example, a push notification. This requires permission for background locations from the user.
The geofence feature has two different trigger types: ENTER and EXIT.
-
ENTERtriggers when the user reaches the bounds of the geofence and enters it. -
EXITtriggers when the user reaches the bounds of the geofence and exits it. Note
NoteBased on our experiences so far, the accuracy of geofencing is inconsistent and can be different based on device types and the environment of usage.
Please be aware that our current geofencing solution only works well when there is no other geofencing solution used in the application.
We recommend to use at least 50m of radius, to ensure that the triggers happen. Based on the Apple documentation only 20 geofences/app can be used.
We recommend adding at least 100m of radius to your geofences, to ensure that the triggers happen. Based on the Android documentation only 100 geofences/app can be used
Only available for iOS
The requestAlwaysAuthorization() method is responsible for asking the required permissions from the user. Calling this method is not necessary, if your app already asked the user for the permissions.
await Emarsys.geofence.requestAlwaysAuthorization();The enable() method is responsible for the activation of this feature
await Emarsys.geofence.enable();The disable() method is responsible for disabling this feature
await Emarsys.geofence.disable();The isEnabled() method returns if the geofencing is currently enabled or not
const isEnabled = await Emarsys.geofence.isEnabled();When setInitialEnterTriggerEnabled() is true, Emarsys SDK will trigger all the affected geofences with Enter type triggers at the moment when the geofence is enabled if the device is already inside that geofence. By default, this value is set to false.
await Emarsys.geofence.setInitialEnterTriggerEnabled(true);You can access the registered geofences from the device using the getRegisteredGeofences() method.
import { type Geofence } from 'react-native-emarsys-sdk';
const geofences: Geofence[] = await Emarsys.geofence.getRegisteredGeofences();