Instalación usando Yarn o NPM:
yarn add emma-react-native-sdk
npm install emma-react-native-sdk
En el caso que la versión de RN sea inferior a 0.60 (si es superior la vinculación es automática):
react-native link emma-react-native-sdk
.xcodeproj
de la ruta node_modules/emma-react-native-sdk/ios/
y sueltalo en la carpeta Libraries del Xcode (para ello es necesario tener abierto el proyecto en Xcode).La versión más reciente del SDK para React Native es la 1.7.1.
Es importante que en AndroidManifest.xml
la activity MainActivity
tenga como atributo launchMode="singleTask"
:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
Para integrar el push hay que añadir el servicio push de EMMA en el AndroidManifest.xml
de la app de Android, dentro del tag <application>
, este servicio es el que monta y muestra la notificacion push:
<service
android:name="io.emma.android.push.EMMAFcmMessagingService"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
Indica el uso del plugin de google-services
en android/buidl.gradle
:
buildscript {
....
repositories {
....
google()
}
dependencies {
...
classpath 'com.google.gms:google-services:4.3.15'
}
}
Una vez el servicio esté incluido añadimos las dependencias de push y aplicamos el plugin de google-services
en el archivo android/app/build.gradle
:
dependencies {
....
implementation 'com.google.firebase:firebase-messaging:21.0.1'
}
apply plugin: 'com.google.gms.google-services'
Importante añadir el google-services.json
obtenido en la consola de Firebase en la ruta android/app/
.
Para más información sobre el push dirigete aquí
Para utilizar el POWLINK o Deeplinking en Android es necesario añadir la siguiente activity al AndroidManifest.xml
de la app, dentro de <application>
:
<activity
android:name="io.emma.android.activities.EMMADeepLinkActivity"
android:noHistory="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="${DEEPLINK_SCHEME}"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="${CUSTOM_POWLINK_DOMAIN}.powlink.io"
android:scheme="https"/>
<data
android:host="${CUSTOM_SHORT_POWLINK_DOMAIN}.pwlnk.io"
android:scheme="https"/>
</intent-filter>
</activity>
Para más información sobre los dominios de POWLINK dirigete aquí.
Debajo de la activity, en el mismo AndroidManifest.xml
, hay que añadir también el siguiente <meta-data>
(reemplazando PACKAGE_NAME por la ruta dónde está MainActivity):
<meta-data
android:name="io.emma.DEEPLINK_OPEN_ACTIVITY"
android:value="${PACKAGE_NAME}.MainActivity"/>
Para descargar e instalar EMMA y sus dependencias hay que ejecutar pod install
en la ruta ios/
de la app. Si el proyecto no incluye CocoaPods
se puede vincular de forma manual como se ha explicado en el apartado Instalación de este artículo.
Para integrar las notificaciones push en iOS hay que activar la Capability > Push Notification
Añade en el AppDelegate.m
o AppDelegate.swift
el bridge de EMMA para React Native.
#import "AppDelegate.h"
// EMMA bridge import
// For static/dynamic frameworks
// #import <emma_react_native_sdk/EmmaReactNative.h>
#import <emma-react-native-sdk/EmmaReactNative.h>
// EMMA bridge import
import emma_react_native_sdk
Añade en el mismo archivo el método para notificar el delegado al bridge de EMMA y añade los métodos delegados para recibir las notificaciones.
#import AppDelegate.h
// EMMA bridge import
#import <emma-react-native-sdk/EmmaReactNative.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//RCT Bridge initialization
// Pass push delegate to EMMA bridge
if (@available(iOS 10.0, *)) {
[EmmaReactNative setPushNotificationsDelegate:self];
}
return YES;
}
//MARK: EMMA - Push methods
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[EmmaReactNative registerToken:deviceToken];
}
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){
[EmmaReactNative didReceiveNotificationResponse:response withActionIdentifier:response.actionIdentifier];
}
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){
[EmmaReactNative willPresentNotification:notification];
}
@end
import UIKit
import UserNotifications
// EMMA bridge import
import emma_react_native_sdk
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//RCT Bridge initialization
// Pass push delegate to EMMA bridge
if #available(iOS 10.0, *) {
EmmaReactNative.setPushNotificationsDelegate(self)
}
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
EmmaReactNative.registerToken(deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NSLog("Error registering notifications " + error.localizedDescription);
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (_ options: UNNotificationPresentationOptions) -> Void) {
EmmaReactNative.willPresent(notification)
completionHandler([.badge, .sound])
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
EmmaReactNative.didReceive(response, withActionIdentifier: response.actionIdentifier)
completionHandler()
}
}
Si queremos que las notificaciones tengan contenido multimedia es necesario crear una extensión como se explica en los siguientes pasos:
Crea la nueva extension añadiendola como target
Añade el nombre de la extensión y haz click en finalizar. En el alert que aparece haz click en cancelar
.
Cancela la activación de la extensión para debug.
En el target de la extensión selecciona la mínima versión soportada, en el caso de extensión de notificaciones sería iOS 10.0.
Ahora hay que vincular la dependencia de EMMA al target de la notificación en el fichero Podfile
, este fichero se encuentra en la ruta ios/
del proyecto.
target 'NotificationService' do
pod 'eMMa', '~> 4.11.4'
end
Vuelve a ejecutar pod install
en la ruta ios/
para vincular la nueva dependencia.
Finalmente, abrimos el proyecto con el .xcworkspace
generado y en el archivo NotificationService.swift
añade el siguiente código:
import UIKit
import UserNotifications
import EMMA_iOS
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
EMMA.didReceiveNotificationRequest(request: request, withNotificationContent: bestAttemptContent) { (content) in
contentHandler(bestAttemptContent)
}
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Por último solo falta añadir las credenciales de la app para poder enviar notificaciones para ello dirigite al siguiente enlace.
Añade en el AppDelegate.m
o AppDelegate.swift
el bridge de EMMA para RN y la librería Linking de RN. Si estás usando Swift en la app, la librería Linking está implementada en objc por lo tanto es necesario añadirla en el fichero App-Bridging-Header.h
creado para hacer de bridge entre objc y Swift.
#import "AppDelegate.h"
#import <React/RCTLinkingManager.h>
// EMMA bridge import
#import <emma-react-native-sdk/EmmaReactNative.h>
// EMMA bridge import
import emma_react_native_sdk
// Imports for EMMA
#import <React/RCTLinkingManager.h>
Añade los siguientes métodos al AppDelegate.m
o AppDelegate.swift
openURL
recoge todos los deeplinks que abren la aplicación.continueUserActivity
recoge los Universal Links que abren la aplicación.#import AppDelegate.h
#import <React/RCTLinkingManager.h>
// EMMA bridge import
#import <emma-react-native-sdk/EmmaReactNative.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//RCT Bridge initialization
return YES;
}
//MARK: EMMA - Deeplinking
- (void)openURL:(NSURL*)url options:(NSDictionary<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion {
[EmmaReactNative handleLink:url];
completion([RCTLinkingManager application:[UIApplication sharedApplication] openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]);
}
//MARK: EMMA - Universal links
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
if (userActivity.webpageURL) {
[EmmaReactNative handleLink:userActivity.webpageURL];
}
return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}
@end
import UIKit
// EMMA bridge import
import emma_react_native_sdk
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//RCT Bridge initialization
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
EmmaReactNative.handleLink(url)
return RCTLinkingManager.application(UIApplication.shared, open: url, options: options)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let webUrl = userActivity.webpageURL {
EmmaReactNative.handleLink(webUrl)
}
return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
Para añadir el POWLINK (Universal Link) dirigete a la sección Capabilities
dentro del target de la app en Xcode y añade Associate domains
, seguidamente añadiremos el POWLINK con el formato applinks:customsubdomain.powlink.io
, notese que customdomain es el subdominio configurado en el Dashboard de EMMA.
En el caso del deeplink básico es necesario añadir el esquema en la sección Info
dentro del target de la app en Xcode y en URL Types
añade una nueva entrada dónde puedes definir el esquema de la app.
Para más información sobre los dominios de powlink y como configurar los dominios en Dashboard dirigete aquí.
Para usar la localización es necesario añadir los siguientes permisos en el archivo Info.plist
de la App:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) needs location access</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) needs location access</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) needs location access</string>
Para solicitar el IDFA es necesario añadir el siguiente permiso al Info.plist
:
<key>NSUserTrackingUsageDescription</key>
<string>Uso del indentificador para redes de terceros</string>
Los ejemplos de está sección se realizan sobre archivos tsx
pero son validos para js
y ts
.
Para importar el sdk en el código usa el siguiente import:
import EmmaSdk from 'emma-react-native-sdk';
En el archivo App.tsx
añadimos:
const handleStartSession = async () => {
console.log('Starting session...');
try {
await EmmaSdk.startSession(startSessionParams);
console.log('Session started on', Platform.OS);
} catch (err) {
console.error('Session failed to start', err);
}
};
useEffect(() => {
handleStartSession();
}, []);
En el caso de no usar el hook useEffect
puedes usar el hook componentDidMount
.
Parámetros de configuración para el inicio de sesión:
Parámetro | Tipo | Descripción |
---|---|---|
sessionKey | string | Key para identificación en el inicio de sesión. |
apiUrl | string (Optional) | Añade la url si se usa un proxy. |
queueTime | int (Optional) | Tiempo para vaciar la cola de operaciones. |
isDebug | boolean (Optional) | Activa/desactiva los logs en el SDK. |
customPowlinkDomains | string[] (Optional) | Añade los dominios si la app usa dominios custom en EMMA (los subdominios que se configuran en preferencias de la app no son dominios custom). |
customShortPowlinkDomains | string[] (Optional) | Añade los dominios cortos si la app usa dominios custom en EMMA (los subdominios que se configuran en preferencias de la app no son dominios custom.) |
trackScreenEvents | boolean (Optional) | Activa/desactiva el seguimiento de pantallas automáticas. |
Añade el startPush en el archivo App.tsx
después del startSession:
const handleStartSession = async () => {
console.log('Starting session...');
try {
await EmmaSdk.startSession(startSessionParams);
EmmaSdk.startPush({
classToOpen: '{PACKAGE_NAME}.MainActivity', //PACKAGE_NAME es el path absoluto de la activity
iconResource: 'notification_icon',
});
console.log('Session started on', Platform.OS);
} catch (err) {
console.error('Session failed to start', err);
}
};
useEffect(() => {
handleStartSession();
}, []);
Parámetros del startPush:
Parámetro | Tipo | Descripción |
---|---|---|
classToOpen | string | Indica la activity de apertura en el caso de hacer click en la notificación, en RN lo normal es tener una solo activity principal por lo tanto añadimos está, para ello siempre la indicamos con el path absoluto {packageName}.MainActivity. |
iconResource | string | El icono que contendrá la notificación en la status bar y en el menú de notificaciones. El SDK busca ese icono de la carpeta drawable o drawable-xxx, por lo tanto es importante que el nombre que se incluya en este parámetro coincida con el png de drawable. |
color | string (Optional) | Color en formato hexadecimal. |
channelId | string (Optional) | Si la aplicación usa un canal de notificaciones ya existente, sino creará uno nuevo. |
channelName | string (Optional) | Añade el nombre del nuevo canal. |
El uso del startPush es válido para Android e iOS, aunque los parámetros solo sean usados en Android ya que en iOS coge los valores por defecto de la App.
Para obtener el deeplink o powlink una vez abierta la aplicación añade los siguientes métodos:
// Listen to deeplink requests
Linking.addEventListener('url', ({ url }) => setDeeplink(url));
// Detect application launchings from a deeplink
const handleInitialDeeplink = () =>
Linking.getInitialURL()
.then((url) => setDeeplink(url || null))
.catch(() => setDeeplink(null));
EMMA permite el envio de eventos personalizados y de varios eventos por defecto, el evento por defecto Apertura
se envia al iniciar sesión en el SDK, pero el evento login y registro son eventos que hay que añadir el código de la aplicación.
Para realizar el envio el de eventos uso el siguiente método:
EmmaSdk.trackEvent({
eventToken: '7b358954cf16bc2b7830bb5307f80f96',
eventAttributes: { ReactNative: 'true' }, // optional
});
Para realizar el registro y/o login es necesario enviar el id del usuario en la app (Customer ID):
const userParams: LoginRegisterUserParams = {
userId: 'user#12345',
};
const handleLoginUser = () => {
EmmaSdk.loginUser(userParams);
};
const handleRegisterUser = () => {
EmmaSdk.registerUser(userParams);
};
EMMA permite el envio de propiedades clave/valor asociadas al dispositivo:
EmmaSdk.trackUserExtraInfo({ userTags: { TAG: 'EMMA_EXAMPLE' } });
Para medir la localización del usuario añade el siguiente método:
EmmaSdk.trackUserLocation();
La localización del usuario solo se recogerá una vez al iniciar sesión.
El proceso de compra consta de varios métodos: startOrder
, addProduct
y trackOrder
.
EmmaSdk.startOrder({
orderId: 'EMMA',
totalPrice: 100,
customerId: 'EMMA',
currencyCode: 'EUR',
});
// Add products
EmmaSdk.addProduct({
productId: 'SDK',
productName: 'SDK',
quantity: 1,
price: 1,
extras: { ReactNative: 'working' },
});
// Commit order
EmmaSdk.trackOrder();
En el caso de cancel una compra que ya se ha realizado usa el método cancelOrder
.
EmmaSdk.cancelOrder('EMMA');
EMMA permite el uso de varios formatos de comunicación:
El formato Banner solo está soportado para Android.
El formato NativeAd devuelve un objeto JSON que contiene el Native Ad. Ejemplo de uso:
const type = INAPP_TYPE.NATIVE_AD
const templateId = 'template1'
try {
const result = await EmmaSdk.inAppMessage({ type, templateId });
// Process Native Ad result
setNativeAds(result);
console.log(`InApp ```${type}``` message`, result);
} catch (err) {
console.error('InApp message error', err);
}
Cualquier formato que no sea Native Ad no espera un resultado, ya que se inyecta automáticamente en la vista. Ejemplo de uso:
try {
await EmmaSdk.inAppMessage({ type });
console.log(`InApp ${type} message`);
} catch (err) {
console.error('InApp message error', err);
}
Los formatos Adball
, Banner
, Startview
y Strip
al ser inyectados envian los eventos Impresión
y Clic
de forma automática cuando se realizan dichas acciones. En el caso del NativeAd al ser un formato gestionado por el desarrollador para notificar estas acciones hay que añadir estos métodos:
// When impression is done
EmmaSdk.sendInAppImpression({
campaignId: nativeAd.id,
type: IN_APP_TYPE.NATIVE_AD,
});
// When click is done
EmmaSdk.sendInAppClick({
campaignId: nativeAd.id,
type: IN_APP_TYPE.NATIVE_AD,
});
El método openNativeAd
permite abrir un NativeAd según la configuración de este. Si se usa este método no hace falta usar el sendInAppClick
ya que está incluido internamente.
const params = {
campaignId: nativeAd.id,
showOn: nativeAd.showOn,
cta, // extracted from fields
};
EmmaSdk.openNativeAd(params);
Para controlar el uso de la ley de protección de datos EMMA pone ha disposición varios métodos para activar/desactivar el seguimiento de los usuarios.
// Enable user tracking
EmmaSdk.enableUserTracking();
//Disable user tracking
EmmaSdk.disableUserTracking(false);
El método disableUserTracking
pone a disposición un flag para eliminar el usuario permanente en el caso de que se deshabilite el seguimiento. Hay que tener en cuenta que al realizar está acción es irreversible y todos los datos asociados al usuario se perderán.
Para comprobar si el seguimiento está activado o desactivado usa el siguiente método:
const isEnabled = await EmmaSdk.isUserTrackingEnabled();
Si en la app no se usan los eventos login/registro para notificar el customerId puedes enviarlo con el siguiente método:
EmmaSdk.setCustomerId(customerId);
Establece manualmente el idioma preferido del usuario.
Este método permite sobrescribir el idioma predeterminado del dispositivo para establecer un idioma personalizado que se utilizará en todas las peticiones del SDK. Esto resulta útil en aplicaciones que permiten al usuario seleccionar un idioma diferente al configurado en el dispositivo.
Se debe usar el código de idioma en formato ISO 639-1: es
(español), en
(inglés), fr
(francés), de
(alemán), it
(italiano), zh-Hans
(chino simplificado), zh-Hant
(chino tradicional), etc.
// En este caso, establece inglés como idioma
EmmaSdk.setUserLanguage("en");
Si no se llama a este método, EMMA utilizará por defecto el idioma preferido del usuario configurado en el sistema del dispositivo.
En el caso de no usar los métodos de push del SDK de EMMA se puede enviar el token de push para realizar envios de notificaciones push, en el caso de que se añadan los métodos del SDK no hace falta usar este método.
EmmaSdk.sendPushToken(token);
Para solicitar el identificador IDFA usa el siguiente método:
EmmaSdk.requestTrackingWithIdfa();
Desde Android 13 para recibir notificationes es necesario solicitar un permiso al usuario. A partir de la versión 1.3.0 se ha añadido el siguiente método para solicitar el permiso:
const permissionStatus = await EmmaSdk.requestNotificationPermission();
Para más informaación sobre SKAdNetwork consulta aquí
Desde la version 1.5.0 se ha añadido soporte para SKAdNetwork 4.0.** El plugin realiza una atribución por defecto que no require integración**. Las intalaciones y eventos activados en el dashboard de EMMA se atribuyen de forma automática, pero en ocasiones se requiere de un método adicional para atribuir de forma manual.
Para desactivar el modo automático:
const startSessionParams = {
....
skanCustomManagementAttribution: false
}
await EmmaSdk.startSession(startSessionParams)
Los métodos para actualizar el valor de conversión:
// Available for iOS 15.5+
await EmmaSdk.updatePostbackConversionValue(5);
const conversionModel = {
conversionValue: 5,
coarseValue: 'high',
lockWindow: false
}
// Available for iOS 16.1+
await EmmaSdk.updatePostbackConversionValue(conversionModel);
conversionValue sería el valor de conversión que identifica a un evento croncreto en la plataforma. Los valores oscilan entre 1 y el 63.
coarseValue representa un segundo valor de tipo string que puede ser: high, medium, low.
lockWindow si es false significa que respeta los plazos de la venta de la tribución, si es true el postback se enviará antes de que termine la ventana.