Puedes añadir el SDK de EMMA con SPM a partir de la versión 4.9.0+
y requiere de Xcode 12 o superior.
Pasos para la integración:
File > Add Package Dependencies...
https://github.com/EMMADevelopment/eMMa-iOS-SDK
y seleccionar la versión 4.12.0+ cómo versión mínimaLa última versión de EMMA está disponible a través de CocoaPods
gem install cocoapods
pod setup
para bajarte el repositorio con todos los “specs" y consultarlos en local, esto directamente creara una copia del repositorio de GitHub.Podfile
pod 'eMMa'
pod install
Para realizar la descarga manual sigue los siguientes pasos:
EMMA_iOS.xcframework
en el siguiente enlace.EMMA_iOS.xcframework
a tu proyecto de Xcode en la sección Frameworks, Libraries,and Embedded Content
.Embed and Sign
.EMMA_iOS.xcframework
en Framwork and Libraries
y seleccional Do Not Embed
.NOTA: Este es el requerimiento mínimo para comenzar a medir instalaciones de tu App.
Session Key
de EMMAEMMA Key
en la página de documentación de EMMAEl lugar donde tiene mas sentido inicializar la librería de EMMA es en el método application(_:didFinishLaunchingWithOptions:)
Primero deberemos importar el módulo de EMMA import EMMA_iOS
y llamar al método (EMMA.startSession(with:)) con los datos de tu session key
Ejemplo de integración básica:
import UIKit
// Import EMMA SDK
import EMMA_iOS
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let configuration = EMMAConfiguration()
configuration.debugEnabled = true
configuration.sessionKey = "MY_EMMA_SESSION_KEY"
EMMA.startSession(with: configuration)
return true
}
}
import SwiftUI
import EMMA_iOS
class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// EMMA uses window to display some InApp Messages
self.window = UIWindow(frame:UIScreen.main.bounds)
let configuration = EMMAConfiguration()
configuration.sessionKey = "MY_EMMA_SESSION_KEY"
EMMA.startSession(with: configuration)
return true
}
}
@main
struct EMMASwiftUiApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
#import "AppDelegate.h"
// Include EMMA SDK Header
#import "EMMA_iOS/EMMA_iOS.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
EMMAConfiguration *configuration = [EMMAConfiguration new];
configuration.sessionKey = @"MY_EMMA_SESSION_KEY";
configuration.debugEnabled = true;
[EMMALegacy startSessionWithConfiguration:configuration];
return YES;
}
@end
El envio de pantallas está activo por defecto en el SDK de EMMA. Para desactivarlo usa la siguiente configuración.
import UIKit
// Import EMMA SDK
import EMMA_iOS
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let configuration = EMMAConfiguration()
configuration.debugEnabled = true
configuration.sessionKey = "MY_EMMA_SESSION_KEY"
configuration.trackScreenEvents = false // disable screens
EMMA.startSession(with: configuration)
return true
}
}
#import "AppDelegate.h"
// Include EMMA SDK Header
#import "EMMA_iOS/EMMA_iOS.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
EMMAConfiguration *configuration = [EMMAConfiguration new];
configuration.sessionKey = @"MY_EMMA_SESSION_KEY";
configuration.debugEnabled = true;
configuration.trackScreenEvents = false; // disable screens
[EMMALegacy startSessionWithConfiguration:configuration];
return YES;
}
@end
Si se está actualizando de una versión anterior del SDK, consulta antes esta página para ver posibles cambios de implementación.
Documentación para integrar la capacidad de gestionar las campañas de captación:
SKAdNetwork (SKAN) es una herramienta lanzada por Apple que permite a los anunciantes medir el rendimiento de la campaña sin vulnerar la privacidad del usuario. Esto quiere decir que no se utilizan identificadores con IDFA o IDFV para hacer un seguimiento del usuario,
tampoco requiere de consentimiento del usuario en el ATT.
Para implementar esta funcionalidad es necesario integrar la versión del SDK 4.12.0 o superior.
Desde iOS 15 Apple permite enviar estas copias de postbacks, no solo a los anunciantes como venia haciendo hasta ahora, sino a cualquier endpoint añadido previamente en la configuración de la app.
¿Cómo funciona el SKAdNetwork? Primero, el anunciante muestra un anuncio de una app (Advertiser app) en su propia aplicación o en una aplicación de terceros (Publisher app). El usuario visualiza el anuncio, le da clic y se descarga la app anunciada en la AppStore. Seguidamente, el usuario abre la app, el SDK de EMMA notifica a SKAdNetwork la apertura de la app en el método EMMA.startSession(with:)
y Apple envia una copia del postback a nuestro dominio añadido en el info.plist
.
Para SKAdNetwork 3.0 e inferiores el postback se envia después de la ventana de atribución que es de 0 a 24 horas. No es posible saber con exactitud cuando se envía ya que Apple lo hace de forma aleatoria en las 24 o 48 horas sucesivas a que termine la ventana de 24 horas.
Para SKAdNetwork 4.0 se envian hasta 3 postbacks después de cada ventana de atribución el envio se realiza 24h-48h después en el primer postback y 24h-144h para el segundo y tercer postback. En este caso tampoco es posible predecir el tiempo de envio ya que lo hace de forma aleatoria dentro de las ventanas. En la siguiente imagen se puede ver la ventana de cada postback y su correspondiente retraso.
Para registrar el dominio de EMMA en el info.plist de la app sigue los siguientes pasos:
Añade la key NSAdvertisingAttributionReportEndpoint
al info.plist
de la app (para más detalles visita la documentación de Apple).
Añade como valor de la key https://emma-skadnetwork.com
. Es importante que el valor tenga exactamente este formato.
Una vez añadido el dominio al info.plist
hay que asegurarse que la app contenga el StoreKit.framework
. El StoreKit.framework
es el framework que se encarga de la comunicación con SKAdNetwork a partir de iOS 14+. Para asegurarse del correcto funcionamiento de la app añade StoreKit.framework
a la app como Opcional.
¿Cómo puedo probar la integración? Es cierto que SKAdNetwork no puede probarse de forma real en entornos de prueba o pre-producción (Testflight) pero si que el SDK de EMMA nos puede dar en estos entornos pistas de su correcta implementación. Para asegurarnos que esta funcionalidad está bien integrada sigue los siguiente pasos:
EMMALogger: 4 - Registering app for SKAdNetwork
.SKAdNetwork: Error while updating conversion value: Error Domain=SKANErrorDomain Code=10 "(null)"
. Este error es normal en entornos de prueba al no funcionar el SKAdNetwork, pero nos da una pista de que la integración es correcta y el StoreKit.framework
está funcionando. Cualquier otro error o log de que la API de SKAdNetwork no está disponible es sintoma de un problema de integración con StoreKit.framework
.Configurar sub-dominio para el powlink
Powlink para iOS utiliza la tecnologíca de Apple para Universal Links. Para poder tener aperturas directas de la aplicación mediante universal links, deberemos configurar un dominio o subdominio único. Consulta la guia de soporte para configurar Powlink en el dashboard de EMMA
Añadir soporte para Universal Links a tu aplicación
Consulta la guia de Apple para añadir los Universal Links a la aplicación
Para el correcto funcionamiento del powlink, EMMA debe conocer todos los links que se abren en la aplicación de forma externa, ya sea desde una URL externa, o desde un deeplink implementado en la aplicación.
Para ello debes implementar el método application(_:continue:restorationHandler:) en tu AppDelegate y ejecutar el método EMMA.handleLink(url: URL).
Procesando los paths de los POWLINKS recibidos podrás lanzar distintas partes de tu aplicación.
Ejemplo de implementación del método EMMA.handleLink(url: URL)
:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
if let url = userActivity.webpageURL {
EMMA.handleLink(url: url)
}
}
return true
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
[EMMALegacy handleLink:userActivity.webpageURL];
}
return YES;
}
Si estas usando un tracker con un dominio que no es el de EMMA (.powlink.io o pwlnk.io), es necesario añadir el dominio al iniciar la librería para que se recoonzca este dominio como un dominio capaz de atribuir campañas.
/*
You can configure custom powlink domains. It can be full domains,
or short link domains
EMMA will treat this domains as their own
*/
configuration.customPowlinkDomains = ["mycustomdomain.com"]
configuration.shortPowlinkDomains = ["pow.link"]
Con EMMA puedes realizar una integración completa del SDK que te permita conocer la localización de tus usuarios, cómo se registran en tu App, cuántas transacciones realizan y hasta sus características propias. Es decir, toda la información de tus usuarios que obtendrás en la sección de Behavior.
La plataforma de EMMA hace la diferenciación entre dos tipos de eventos. Los que la plataforma incluye por defecto y los eventos Custom que quieras integrar según la estructura de tu aplicación.
https://docs.emma.io/es/primeros-pasos/eventos#eventos-por-defecto
Sigue los siguientes pasos para su integración:
El método EMMA.registerUser(userId:forMail:andExtras:) permite enviar información sobre los registros en la aplicación
Ejemplo:
func register() {
EMMA.registerUser("554234", forMail: "test@emma.io")
}
- (void) registerUser {
[EMMALegacy registerUser:@"554234" forMail:@"test@emma.io"];
}
El método EMMA.loginUser(userId:forMail:andExtras:) permite enviar información sobre los eventos login
.
Si tenemos un evento login
sucesivo con los mismos datos, podemos utilizar el método EMMA.loginDefault(). Este método sería útil en el caso de un "Auto-Login" por ejemplo.
func login() {
EMMA.loginUser("554234", forMail: "test@emma.io")
}
- (void) registerUser {
[EMMALegacy loginUser:@"554234" forMail:@"test@emma.io"];
}
Usa EMMA.trackEvent(request:) para contar el número de veces que ciertos eventos suceden durante una sesión en tu app.
Esto puede ser útil para medir cuántas veces los usuarios convierten en diferentes acciones, por ejemplo.
Puedes obtener los tokens de eventos creándolos en la plataforma de EMMA. Si un token no existente es enviado a EMMA se devolverá un error.
Para más información sobre eventos consulta la documentación aquí.
if let eventRequest = EMMAEventRequest.init(token: "<token>") {
// Optional: You can add your custom event attributes
eventRequest.attributes = ["test_attribute":"test_value"]
// Optional. You can capture emma requests with this delegate
eventRequest.requestDelegate = self
// Optional. Append your request ID to capture it later
eventRequest.customId = "MY_EVENT_REQUEST"
EMMA.trackEvent(eventRequest)
}
EMMAEventRequest * request = [[EMMAEventRequest alloc] initWithToken:@"<token>"];
// Optional: You can add your custom event attributes
request.attributes = @{@"test_attribute": @"test_value"};
// Optional. You can capture emma requests with this delegate
request.requestDelegate = self;
// Optional. Append your request ID to capture it later
request.customId = @"MY_EVENT_REQUEST";
[EMMALegacy trackEvent:request];
Para la request de eventos se puede añadir un delegado que controle el estado de la petición y si esta devuelve datos (p.e una rule) o no.
Ejemplo:
class EventExample: NSObject, EMMARequestDelegate {
func sendAdvancedEvent() {
let eventRequest = EMMAEventRequest.init(token: "<token>")
// Optional: You can add your custom event attributes
eventRequest?.attributes = ["test_attribute":"test_value"]
// Optional. You can capture emma requests with this delegate
eventRequest?.requestDelegate = self
// Optional. Append your request ID to capture it later
eventRequest?.customId = "MY_EVENT_REQUEST"
EMMA.trackEvent(eventRequest)
}
/****** EMMARequestDelegate Protocol ********/
func onStarted(_ id: String!) {
if id == "MY_EVENT_REQUEST" {
print("Request for MY_EVENT_REQUEST started")
}
}
func onSuccess(_ id: String!, containsData data: Bool) {
if id == "MY_EVENT_REQUEST" {
print("Request for MY_EVENT_REQUEST succeed")
}
}
func onFailed(_ id: String!) {
if id == "MY_EVENT_REQUEST" {
print("Request for MY_EVENT_REQUEST failed")
}
}
}
// ViewController.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface ViewController : UIViewController<EMMARequestDelegate>
@end
// ViewController.m
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void) sendAdvancedEvent {
EMMAEventRequest * request = [[EMMAEventRequest alloc] initWithToken:@"<token>"];
// Optional: You can add your custom event attributes
request.attributes = @{@"test_attribute": @"test_value"};
// Optional. You can capture emma requests with this delegate
request.requestDelegate = self;
// Optional. Append your request ID to capture it later
request.customId = @"MY_EVENT_REQUEST";
[EMMALegacy trackEvent:request];
}
- (void)onFailed:(NSString *)requestId {
if ([requestId isEqualToString:@"MY_EVENT_REQUEST"]) {
NSLog(@"Request for MY_EVENT_REQUEST failed");
}
}
- (void)onStarted:(NSString *)requestId {
if ([requestId isEqualToString:@"MY_EVENT_REQUEST"]) {
NSLog(@"Request for MY_EVENT_REQUEST started");
}
}
- (void)onSuccess:(NSString *)requestId containsData:(BOOL)data {
if ([requestId isEqualToString:@"MY_EVENT_REQUEST"]) {
NSLog(@"Request for MY_EVENT_REQUEST success");
}
}
@end
EMMA permite medir cualquier transacción o compra que se realice en tu app. Este es el ejemplo paramedir una transacción:
func trackTransaction() {
EMMA.startOrder("<ORDER_ID>", customerId: "<CUSTOMER_ID>", totalPrice: 10.0, coupon: "")
EMMA.addProduct("<PRODUCT_ID>", name: "<PRODUCT_NAME>", qty: 1.0, price: 10.0)
EMMA.trackOrder()
}
- (void) trackTransaction {
[EMMALegacy startOrder:@"<ORDER_ID>" customerId:@"<CUSTOMER_ID>" totalPrice:10.0 coupon:@""];
[EMMALegacy addProduct:@"<PRODUCT_ID>"name:@"<PRODUCT_NAME>" qty:1.0 price:10.0];
[EMMALegacy trackOrder];
}
El método para iniciar la transacción es EMMA.startOrder(orderId:andCustomer:withTotalPrice:withExtras:assignCoupon:)
EMMA.startOrder("<ORDER_ID>", customerId: "<CUSTOMER_ID>", totalPrice: 10.0, coupon: "")
[EMMALegacy startOrder:@"<ORDER_ID>" customerId:@"<CUSTOMER_ID>" totalPrice:10.0 coupon:@""];
Una vez iniciada la transacción hay que añadir los productos a la misma. Para ello usaremos el método EMMA.addProduct(productId:andName:withQty:andPrice:withExtras:)
EMMA.addProduct("<PRODUCT_ID>", name: "<PRODUCT_NAME>", qty: 1.0, price: 10.0)
[EMMALegacy addProduct:@"<PRODUCT_ID>"name:@"<PRODUCT_NAME>" qty:1.0 price:10.0];
Una vez tenemos todos los productos añadidos, ejecutamos la medición de la transacción con el método EMMA.trackOrder()
EMMA.trackOrder()
[EMMALegacy trackOrder];
En el caso de que se necesite cancelar el tracking de una transacción usaremos el método EMMA.cancelOrder(orderId:)
func cancelTransaction() {
EMMA.cancelOrder("<ORDER_ID>")
}
-(void) cancelTransaction {
[EMMALegacy cancelOrder:@"<ORDER_ID>"];
}
El método EMMA.trackExtraUserInfo(info:) actualiza o añade parámetros extra de cara a obtener mejor segmentación en el filtrado Users with tag
. Puede ser usado en el registro, en el login o en cualquier otra sección de la app donde se recoja información del usuario.
Si quieres usar la EMMA RULE
On his Birthday
envía la fecha de cumpleaños con el método para el TAG en el siguiente formato (ISO):
Nombre:BIRTHDAY
Valor:YYYY-MM-DD
Recuerda revisar los logs del SDK para consultar el listado de TAGS que no pueden ser usados por estar reservados para el sistema de EMMA.
func trackUserInfo() {
//In this example we set the tag "AGE" of our user to value "40"
EMMA.trackExtraUserInfo(["AGE" : "40"])
}
-(void) trackUserInfo {
[EMMALegacy trackExtraUserInfo:@{@"AGE": @"40"}];
}
EMMA puede adjuntar la localización actual del usuario, si la aplicación dispone de permisos con el método EMMA.trackLocation()
Podemos recuperar el id de usuario de EMMA con el método EMMA.getUserId(resultBlock:)
Este método devuelve el EMMA ID como un String. Este ID es único para cada usuario y puede ser utilizado para filtrar cuando se envían comunicaciones.
func getUserID() {
/* This method gets EMMA user id */
EMMA.getUserId { (user_id) in
guard let uid = user_id else {
print("Error getting user id")
return
}
print("Our EMMA USER ID is \(uid)")
}
}
-(void) getUserId {
[EMMALegacy getUserId:^(NSString *userId) {
if (userId) {
NSLog(@"User id is %@", userId);
}
}]
}
El formato del identificador es de tipo UUID V4. Para obtener el identificador del dispositivo usa el siguiente método:
EMMA.deviceId()
[EMMALegacy deviceId];
Para enviar el customer ID independientemente del login/registro usa el siguiente método:
EMMA.setCustomerId(customerId: "<Customer ID>")
[EMMALegacy setCustomerId:@"<Customer ID>"];
El método EMMA.getUserInfo(resultBlock:) recupera el perfil del usuario que tenemos registrado en EMMA.
Los datos que devuelve la llamada se obtienen de la propia información que recopila el SDK. En un primer arranque puede tardar unos segundos en recopilar la información, si la llamada se realiza justo después del startSession podría devolver nulo.
Referencia de los campos del perfil de usuario
func getUserInfo() {
/* This method retrieves a json representation
of emma's user profile */
EMMA.getUserInfo { (user_profile) in
guard let profile = user_profile else {
print("Error getting user profile")
return
}
print("Retrieved user profile \(profile)")
}
}
-(void) getUserInfo {
[EMMALegacy getUserInfo:^(NSDictionary *userInfo) {
if (userInfo) {
NSLog(@"Retrieved user profile %@", userInfo);
}
}]
}
A través del método EMMA.installAttributionInfo(attributionDelegate:) podremos obtener los datos de la atribución de la instalación de cada usuario.
Consulta la descripción de los campos de atribución para ver la información disponible.
A partir de la versión 4.6.2 la campaña recibe los parámetros del click a través del método clickParams.
Para poder obtener información sobre la atribución de la instalación, se puede seguir el siguiente ejemplo:
import UIKit
import EMMA_iOS
class AttributionInfoExampleViewController: UIViewController, EMMAInstallAttributionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//We can get EMMA Attribution Info
EMMA.installAttributionInfo(self)
}
// MARK: - EMMA Attribution Delegate
func onAttributionReceived(_ attribution: EMMAInstallAttribution!) {
guard let info = attribution else {
print("Error getting attribution info")
return
}
print("Received attribution info \(info)")
}
}
// AttributionInfoExampleViewController.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface AttributionInfoExampleViewController : UIViewController<EMMAInstallAttributionDelegate>
@end
// AttributionInfoExampleViewController.m
#import "AttributionInfoExampleViewController.h"
@interface AttributionInfoExampleViewController ()
@end
@implementation AttributionInfoExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[EMMALegacy installAttributionInfo:self];
}
- (void)onAttributionReceived:(EMMAInstallAttribution *)attribution {
if (attribution) {
NSLog(@"Received attribution info %@", attribution);
}
}
@end
Para diferenciar una notificación de nuestro sistema de push respecto otros sistemas, el payload enviado por EMMA contiene un flag denominado "eMMa".
EMMA te permite añadir un potente sistema de Notificaciones Push fácil de integrar. La plataforma también permite enviar info a través de las notificaciones y generar cualquier acción dentro de tu app gracias a ellas.
El APNs Auth Key de Apple es un tipo de certificado (.p8) que remplazada a sus antecesores, el certificado APNs de producción y el certificado APNs de desarrollo. Desde EMMA recomendamos el uso de este certificado por los siguiente motivos:
Para el uso de este certificado es necesario:
Como obtener el push auth key en Apple
Una vez generados los certificados de tu app, ya puedes empezar a integrar las Notificaciones Push. A continuación tenemos un ejemplo de un AppDelegate completo con la integración del push:
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, EMMAPushDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
configuration.pushNotificationsDelegate = self
//Enable EMMA Push System
EMMA.startPushSystem()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
EMMA.registerToken(deviceToken)
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (_ options: UNNotificationPresentationOptions) -> Void) {
if #available(iOS 14.0, *) {
completionHandler([.badge, .sound, .banner, .list])
} else {
completionHandler([.badge, .sound, .alert])
}
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
EMMA.handlePush(response.notification.request.content.userInfo)
completionHandler()
}
}
// AppDelegate.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface AppDelegate : UIResponder <UNUserNotificationCenterDelegate, EMMAPushDelegate>
@end
// AppDelegate.m
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface AppDelegate : UIResponder <UNUserNotificationCenterDelegate, EMMAPushDelegate>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
EMMAConfiguration *configuration = [EMMAConfiguration new];
configuration.sessionKey = @"MY_EMMA_SESSION_KEY";
configuration.debugEnabled = true;
configuration.trackScreenEvents = false; // disable screens
configuration.pushNotificationsDelegate = self;
[EMMALegacy startSessionWithConfiguration:configuration];
[EMMALegacy startPushSystem];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
[EMMALegacy registerToken:deviceToken];
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
if (@available(iOS 14, *)) {
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
} else {
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
}
-(void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[EMMALegacy handlePush:response.notification.request.content.userInfo];
completionHandler();
}
@end
- (void)onPushOpen:(EMMAPush *)push {
if (push) {
NSLog(@"Obtained push campaign %@", push);
}
}
Para mantener los valores del badge entre la extensión y la aplicación es necesario añadir a las Capabilities de la app un nuevo AppGroup con el siguiente formato group.YOUR_BUNDLE_ID.emma
Para registrar el token es necesario activar las Notificaciones Push en Capabilities.
Para resetear el badge que cuenta el número de notificaciones recibidas, existen dos formas:
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
[UIApplication sharedApplication].applicationIconBadgeNumber = 0
// Clear with push system start
let options = EMMAPushOptions()
options.badgeClearing = true
EMMA.startPushSystem(with: options)
// Clear anywhere in the app
EMMAPushBadgeController.clearBadge(fromOpen: true)
// Clear with push system start
EMMAPushOptions * pushOptions = [EMMAPushOptions new];
pushOptions.badgeClearing = true
[EMMALegacy startPushSystemWithOptions:pushOptions];
// Clear anywhere in the app
[EMMAPushBadgeController clearBadgeFromOpen:true];
El badge se guarda en preferencias del usuario. Para sincronizar esas preferencias entre la app y la extensión es necesario configurar un App Group en "Capabilities", tanto en el target de la App como en la extensión. El formato del bundle tiene que ser "group.{bundleId}.emma", donde bundleId es el Bundle ID del target App.
Las Rich Push Notifications se habilitan mediante una Notification Service Extension, un binario aparte dentro del paquete de tu aplicación. Antes de mostrar una nueva notificación push, el sistema llamará a tu Notication Service Extension permitiéndote modificar el contenido y los adjuntos que serán mostrados.
Para crear la Notification Service Extension en tu proyecto, en Xcode eliges File -> New -> Target
y eliges la plantilla de la Notification Service Extension.
Puedes nombrar la extensión a tu gusto, la llamaremos RichPushExtension para esta guia. Asegúrate de incluir la extensión en tu aplicación.
Al pulsar en Finish se te preguntará para activar la extensión, haz click en Activate para finalizar.
Al acabar estos pasos, tres nuevos archivos han sido añadidos al directorio con el nombre de la extensión que hemos creado (RichPushExtension en nuestro caso): NotificationService.h, NotificationService.m and Info.plist
Por último asegúrate de que estan habilitadas las notificaciones push para la extensión que hemos creado. Selecciona tu nueva extension (RichPushExtension), elige Capabilities y activa Push Notifications.
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)
}
}
}
#import "NotificationService.h"
#import "EMMA_iOS/EMMA_iOS.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
[EMMALegacy didReceiveNotificationRequest:request withNotificationContent:_bestAttemptContent AndCompletionHandler:^(UNNotificationContent *) {
contentHandler(self.bestAttemptContent);
}];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
self.contentHandler(self.bestAttemptContent);
}
@end
Para añadir el SDK a la extensión es necesario modificar el archivo Podfile añadiendo el pod eMMa al objetivo de la extensión:
target 'ProjectName' do
pod 'eMMa', '~> 4.10.1'
end
target 'EMMANotificationServiceExtension' do
pod 'eMMa', '~> 4.10.1'
end
Para usar sonidos personalizados en las notificaciones que envíes con EMMA, tienes que añadir los archivos de sonido .caf que quieras a tu aplicación, en la raíz del paquete de tu aplicación o en Library/Sounds/. Recuerda que debes emplear para los sonidos los mismos nombres de archivo en iOS y Android.
Puedes redireccionar las aperturas de las notificaciones push a una sección en tu app. Para ello debes usar una estructura como esta:
scheme://host/page1/page2/page3
A partir de la versión del SDK 4.10.x se ha añadido la funcionalidad de botones con acciones en las notificaciones. Para poder traquear la acción desde la cual se abre la notificación hay que reemplazar el handlePush que estamos usando hasta ahora por este:
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
EMMA.handlePush(userInfo: response.notification.request.content.userInfo,
actionIdentifier: response.actionIdentifier
)
completionHandler()
}
-(void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[EMMALegacy handlePush:response.notification.request.content.userInfo withActionIdentifier:response.actionIdentifier];
completionHandler();
}
Para asegurar el correcto funcionamiento hay que comprobar que el SDK que contiene el servicio de notificaciones tiene la versión 4.10.x y está alineado con la versión de la app.
Consulta aquí para configurar un push con estás acciones.
A continuación tenemos un ejemplo completo de la integración de campañas In-App con EMMA.
import UIKit
import EMMA_iOS
class InAppExampleViewController: UIViewController, EMMAInAppMessageDelegate {
override func viewDidLoad() {
super.viewDidLoad()
getStartView()
// You can add as many request delegates as you want
EMMA.add(inAppDelegate: self)
//Check if any adball is showing
if EMMA.isAdBallShowing() {
print("There is an adball floating arround")
}
}
// MARK: - EMMA InApp Messages Requests
func getStartView() {
let startViewinAppRequest = EMMAInAppRequest(type: .Startview)
// Optional. You can filter by label
startViewinAppRequest?.label = "<LABEL>"
/*
By default Startview presents on UIApplication.shared.delegate?.window?.rootViewController
You can customize this behavior uncommenting following line
*/
//EMMA.setRootViewController(UIViewController!)
EMMA.inAppMessage(startViewinAppRequest)
}
func getBanner() {
let bannerRequest = EMMAInAppRequest(type: .Banner)
EMMA.inAppMessage(bannerRequest)
}
func getAdBall() {
let adballRequest = EMMAInAppRequest(type: .Adball)
EMMA.inAppMessage(adballRequest)
}
func getDynamicTabBar() {
// You must define your UITabBarController
// Uncomment following line!
// EMMA.setPromoTabBarController(UITabBarController!)
// Sets default promo tab index if not defined in EMMA Platform
EMMA.setPromoTabBarIndex(5)
// Sets a tab bar item to be shown if not defined in EMMA Platform
// EMMA.setPromoTabBarItem(UITabBarItem!)
let dynamicTabBarRequest = EMMAInAppRequest(type: .PromoTab)
EMMA.inAppMessage(dynamicTabBarRequest)
}
func getStrip() {
let stripRequest = EMMAInAppRequest(type: .Strip)
EMMA.inAppMessage(stripRequest)
}
// MARK: - EMMA InApp Message Delegate
func onShown(_ campaign: EMMACampaign!) {
guard let c = campaign else {
print("Error getting campaign info")
return
}
print("Shown campaign \(c)")
}
func onHide(_ campaign: EMMACampaign!) {
guard let c = campaign else {
print("Error getting campaign info")
return
}
print("Hide campaign \(c)")
}
func onClose(_ campaign: EMMACampaign!) {
guard let c = campaign else {
print("Error getting campaign info")
return
}
print("Closed campaign \(c)")
}
}
// InAppExampleViewController.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface InAppExampleViewController : UIViewController<EMMAInAppMessageDelegate>
@end
// InAppExampleViewController.m
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
#import "InAppExampleViewController.h"
@interface InAppExampleViewController ()
@end
@implementation InAppExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self getStartView];
[EMMALegacy addInAppDelegate:self];
if ([EMMALegacy isAdBallShowing]) {
NSLog(@"There is an adball floating arround");
}
}
-(void) getStartView {
/* By default Startview presents on UIApplication.shared.delegate?.window?.rootViewController
You can customize this behavior uncommenting following line */
//[EMMALegacy setRootViewController:(UIViewController *)]
EMMAInAppRequest *startViewRequest = [[EMMAInAppRequest alloc] initWithType: Startview];
[EMMALegacy inAppMessage:startViewRequest];
}
-(void) getBanner {
EMMAInAppRequest *bannerRequest = [[EMMAInAppRequest alloc] initWithType:Banner];
[EMMALegacy inAppMessage:bannerRequest];
}
-(void) getAdBall {
EMMAInAppRequest * adballRequest = [[EMMAInAppRequest alloc] initWithType: Adball];
[EMMALegacy inAppMessage:adballRequest];
}
-(void) getDynamicTarBar {
// You must define your UITabBarController
// Uncomment following line!
//[EMMALegacy setPromoTabBarController:<#(UITabBarController *)#>];
// Sets default promo tab index if not defined in EMMA Platform
[EMMALegacy setPromoTabBarIndex:5];
// Sets a tab bar item to be shown if not defined in EMMA Platform
//[EMMALegacy setPromoTabBarItem:<#(UITabBarItem *)#>];
EMMAInAppRequest * dynamicRequest = [[EMMAInAppRequest alloc] initWithType:PromoTab];
[EMMALegacy inAppMessage:dynamicRequest];
}
-(void) getStrip {
EMMAInAppRequest * stripRequest = [[EMMAInAppRequest alloc] initWithType: Strip];
[EMMALegacy inAppMessage:stripRequest];
}
- (void)onClose:(EMMACampaign *)campaign {
if (campaign) {
NSLog(@"Closed campaign %@", campaign);
}
}
- (void)onHide:(EMMACampaign *)campaign {
if (campaign) {
NSLog(@"Hide campaign %@", campaign);
}
}
- (void)onShown:(EMMACampaign *)campaign {
if (campaign) {
NSLog(@"Shown campaign %@", campaign);
}
}
@end
Los Banners de EMMA te permiten mostrar un banner en tu app con información promocional customizada. El banner será mostrado en función de la configuración que establezcas en el dashboard de EMMA. Es una comunicación que permite mostrarle al usuario una webview externa con contenido HTML o redireccionar a otra pestaña de la aplicación (vía deeplink).
Para mostrar los Banners en tu app necesitas al menos integrar el primer método descrito a continuación:
Se recomienda hacer la llamada dentro de la función
viewDidLayoutSubviews
func getBanner() {
let bannerRequest = EMMAInAppRequest(type: .Banner)
EMMA.inAppMessage(bannerRequest)
}
-(void) getBanner {
EMMAInAppRequest *bannerRequest = [[EMMAInAppRequest alloc] initWithType:Banner];
[EMMALegacy inAppMessage:bannerRequest];
}
La Startview de EMMA te permite mostrar información en HTML al inicio de tu como una webview.
Para permitir el uso de Startviews debes integrar:
func getStartView() {
let startViewinAppRequest = EMMAInAppRequest(type: .Startview)
// Optional. You can filter by label
startViewinAppRequest?.label = "<LABEL>"
/*
By default Startview presents on UIApplication.shared.delegate?.window?.rootViewController
You can customize this behavior uncommenting following line
*/
//EMMA.setRootViewController(UIViewController!)
EMMA.inAppMessage(startViewinAppRequest)
}
-(void) getStartView {
/* By default Startview presents on UIApplication.shared.delegate?.window?.rootViewController
You can customize this behavior uncommenting following line */
//[EMMALegacy setRootViewController:(UIViewController *)]
EMMAInAppRequest *startViewRequest = [[EMMAInAppRequest alloc] initWithType: Startview];
[EMMALegacy inAppMessage:startViewRequest];
}
EMMA AdBall te permite mostrar un pequeño gráfico circular en su aplicación que el usuario puede desplazar libremente por la pantalla de inicio de la app y también cerrarla. Si se presiona sobre este aparece un pop-up con contenido HTML.
func getAdBall() {
let adballRequest = EMMAInAppRequest(type: .Adball)
EMMA.inAppMessage(adballRequest)
}
-(void) getStartView {
/* By default Startview presents on UIApplication.shared.delegate?.window?.rootViewController
You can customize this behavior uncommenting following line */
//[EMMALegacy setRootViewController:(UIViewController *)]
EMMAInAppRequest *startViewRequest = [[EMMAInAppRequest alloc] initWithType: Startview];
[EMMALegacy inAppMessage:startViewRequest];
}
Utilizar para comprobar si el AdBall se está mostrando en la pantalla del dispositivo. Devuelve BOOL true si se está mostrando en la pantalla.
//Check if any adball is showing
if EMMA.isAdBallShowing() {
print("There is an adball floating arround")
}
//Check if any adball is showing
if ([EMMALegacy isAdBallShowing]) {
NSLog(@"There is an adball floating arround");
}
El EMMA tabBar View (DynamicTab) te permite mostrar información en HTML en una nueva sección de su aplicación (sólo en el caso de que ya utilice un TabBar en su app). El nuevo TabBar Item puede ser creado localmente o en la plataforma de EMMA.
Si se realiza de ambas formas, prevalecerá el TabBar Item dinámico creado directamente en EMMA .
Para habilitar esta posibilidad en su app necesitará implementar al menos el primer método. Se puede configurar siguiendo estas indicaciones:
El índex del ítem del TabBar y el ítem propiamente dicho puede ser especificado en la plataforma de EMMA. Cómo se indicaba anteriormente, la configuración en EMMA prevalecerá siempre sobre la configuración local.
func getDynamicTabBar() {
// You must define your UITabBarController
// Uncomment following line!
// EMMA.setPromoTabBarController(UITabBarController!)
// Sets default promo tab index if not defined in EMMA Platform
EMMA.setPromoTabBarIndex(5)
// Sets a tab bar item to be shown if not defined in EMMA Platform
// EMMA.setPromoTabBarItem(UITabBarItem!)
let dynamicTabBarRequest = EMMAInAppRequest(type: .PromoTab)
EMMA.inAppMessage(dynamicTabBarRequest)
}
-(void) getDynamicTarBar {
// You must define your UITabBarController
// Uncomment following line!
//[EMMALegacy setPromoTabBarController:<#(UITabBarController *)#>];
// Sets default promo tab index if not defined in EMMA Platform
[EMMALegacy setPromoTabBarIndex:5];
// Sets a tab bar item to be shown if not defined in EMMA Platform
//[EMMALegacy setPromoTabBarItem:<#(UITabBarItem *)#>];
EMMAInAppRequest * dynamicRequest = [[EMMAInAppRequest alloc] initWithType:PromoTab];
[EMMALegacy inAppMessage:dynamicRequest];
}
El EMMA Strip te permite mostrar un banner de texto sobre donde se sitúa habitualmente el Status Bar con un mensaje para sus usuarios. Este puede ser desplegado en cualquier pantalla de la app.
func getStrip() {
let stripRequest = EMMAInAppRequest(type: .Strip)
EMMA.inAppMessage(stripRequest)
}
-(void) getStrip {
EMMAInAppRequest * stripRequest = [[EMMAInAppRequest alloc] initWithType: Strip];
[EMMALegacy inAppMessage:stripRequest];
}
EMMA Coupons te permite obtener, verificar, canjear cupones que se hayan definido y configurado en la plataforma de EMMA.
Se devolverá un listado de los cupones existentes listándose primero los cupones automáticos ordenados de más reciente a más antiguos y después se listarán los cupones clásicos ordenados también de más reciente a más antiguo.
En el block response, obtendremos un diccionario con la información relativa a cada coupon disponible para el usuario. Para cada coupon se dispone de la siguiente información: id (identificador interno de EMMA), código, número máximo de canjeos, número de veces canjeado, título, descripción, imagen,...
import UIKit
import EMMA_iOS
class CouponExampleViewController: UIViewController, EMMACouponDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Add coupon delegate
EMMA.add(self)
}
func getAllCoupons() {
let couponsRequest = EMMAInAppRequest(type: .Coupons)
EMMA.inAppMessage(couponsRequest)
}
func sentCouponClick(_ coupon: EMMACoupon) {
EMMA.sendClick(.campaignCoupon, withId: String(coupon.couponId))
}
func getSingleCoupon() {
let couponsRequest = EMMAInAppRequest(type: .Coupons)
couponsRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(couponsRequest)
}
func checkCouponRedeems() {
let couponsRequest = EMMAInAppRequest(type: .CouponValidRedeems)
// You must pass coupon id to check
couponsRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(couponsRequest)
}
func redeemCoupon() {
let redeemCouponRequest = EMMAInAppRequest(type: .RedeemCoupon)
redeemCouponRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(redeemCouponRequest)
}
func cancelCoupon() {
let cancelCouponRequest = EMMAInAppRequest(type: .CancelCoupon)
cancelCouponRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(cancelCouponRequest)
}
// MARK: - EMMA Coupon Delegate methods
func onCouponsReceived(_ coupons: [EMMACoupon]!) {
guard let receivedCoupons = coupons else {
print("Error retrieving coupons")
return
}
// Now we can send coupon impressions
receivedCoupons.forEach { (coupon) in
EMMA.sendImpression(.campaignCoupon, withId: String(coupon.couponId))
}
print("Received coupons \(receivedCoupons)")
}
func onCouponsFailure() {
print("Error retrieving coupons")
}
func onCouponValidRedeemsReceived(_ validRedeems: Int) {
if validRedeems > 0 {
print("Coupon have valid redeems pending \(validRedeems)")
}
}
}
// CouponExampleViewController.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface CouponExampleViewController : UIViewController<EMMACouponDelegate>
@end
// CouponExampleViewController.m
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
#import "CouponExampleViewController.h"
@interface CouponExampleViewController ()
@end
@implementation CouponExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
[EMMALegacy addCouponDelegate:self];
}
-(void) getAllCoupons {
EMMAInAppRequest * couponsRequest = [[EMMAInAppRequest alloc] initWithType:Coupons];
[EMMALegacy inAppMessage:couponsRequest];
}
-(void) sendCouponClick:(EMMACoupon*) coupon {
[EMMALegacy sendClick:kCampaignCoupon withId:[@(coupon.couponId) stringValue]];
}
-(void) getSingleCoupon {
EMMAInAppRequest * couponRequest = [[EMMAInAppRequest alloc] initWithType:Coupons];
couponRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:couponRequest];
}
-(void) checkCouponsRedeem {
EMMAInAppRequest *couponsRequest = [[EMMAInAppRequest alloc] initWithType:CouponValidRedeems];
couponsRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:couponsRequest];
}
-(void) redeemCoupon {
EMMAInAppRequest *redeemRequest = [[EMMAInAppRequest alloc] initWithType:RedeemCoupon];
redeemRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:redeemRequest];
}
-(void) cancelCoupon {
EMMAInAppRequest * cancelCouponRequest = [[EMMAInAppRequest alloc] initWithType: CancelCoupon];
cancelCouponRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:cancelCouponRequest];
}
-(void)onCouponsFailure {
NSLog(@"Error retrieving coupons");
}
-(void)onCouponsReceived:(NSArray<EMMACoupon *> *)coupons {
for (EMMACoupon * coupon in coupons) {
NSLog(@"Coupon %lo received", coupon.couponId);
// Show coupon
[EMMALegacy sendImpression: kCampaignCoupon withId:[@(coupon.couponId) stringValue]];
}
}
-(void) onCouponValidRedeemsReceived:(int)validRedeems {
if (validRedeems > 0) {
NSLog(@"Coupon have valid redeems pending %d", validRedeems);
}
}
@end
Con esta llamada, obtendremos la información relativa a un coupon concreto.
El parámetro couponId debe ser el identificador interno de EMMA de un coupon, identificador que se puede obtener de una llamada a Coupons hecha con anterioridad.
En el block response, obtendremos un diccionario con la información relativa al coupon consultado: id (identificador interno de EMMA), código, número máximo de canjeos, número de veces canjeado, título, descripción, imagen …
func getSingleCoupon() {
let couponsRequest = EMMAInAppRequest(type: .Coupons)
couponsRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(couponsRequest)
}
func onCouponsReceived(_ coupons: [EMMACoupon]!) {
guard let receivedCoupons = coupons else {
print("Error retrieving coupons")
return
}
// Now we can send coupon impressions
receivedCoupons.forEach { (coupon) in
EMMA.sendImpression(.campaignCoupon, withId: String(coupon.couponId))
}
print("Received coupons \(receivedCoupons)")
}
-(void) getSingleCoupon {
EMMAInAppRequest * couponRequest = [[EMMAInAppRequest alloc] initWithType:Coupons];
couponRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:couponRequest];
}
-(void)onCouponsReceived:(NSArray<EMMACoupon *> *)coupons {
for (EMMACoupon * coupon in coupons) {
NSLog(@"Coupon %lo received", coupon.couponId);
// Show coupon
[EMMALegacy sendImpression: kCampaignCoupon withId:[@(coupon.couponId) stringValue]];
}
}
Con esta llamada podemos comprobar si el usuario puede canjear el coupon indicado.
El parámetro couponId debe ser el identificador interno de EMMA de un coupon, identificador que se puede obtener de una llamada a checkForCoupons hecha con anterioridad.
En el block response, nos indicará el número de veces que el usuario todavía puede canjear el coupon.
func checkCouponRedeems() {
let couponsRequest = EMMAInAppRequest(type: .CouponValidRedeems)
// You must pass coupon id to check
couponsRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(couponsRequest)
}
func onCouponValidRedeemsReceived(_ validRedeems: Int) {
if validRedeems > 0 {
print("Coupon have valid redeems pending \(validRedeems)")
}
}
-(void) checkCouponsRedeem {
EMMAInAppRequest *couponsRequest = [[EMMAInAppRequest alloc] initWithType:CouponValidRedeems];
couponsRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:couponsRequest];
}
-(void) onCouponValidRedeemsReceived:(int)validRedeems {
if (validRedeems > 0) {
NSLog(@"Coupon have valid redeems pending %d", validRedeems);
}
}
Con esta llamada, el usuario canjea el coupon indicado.
El parámetro couponId debe ser el identificador interno de EMMA de un coupon, identificador que se puede obtener de una llamada a Coupons hecha con anterioridad.
func redeemCoupon() {
let redeemCouponRequest = EMMAInAppRequest(type: .RedeemCoupon)
redeemCouponRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(redeemCouponRequest)
}
-(void) redeemCoupon {
EMMAInAppRequest *couponsRequest = [[EMMAInAppRequest alloc] initWithType:CouponValidRedeems];
couponsRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:couponsRequest];
}
Con esta llamada se puede cancelar el canjeo de un coupon realizado con anterioridad.
El parámetro couponId debe ser el identificador interno de EMMA de un coupon identificador que se puede obtener en la llamada de tipo Coupons.
Opcionalmente se puede indicar un parámetro count si se quiere cancelar más de un canjeo realizado con anterioridad, si no se indica, se cancelará una vez el coupon.
El error 511 informa de que el redeem no se ha podido completar por alguna razón. Algunas posibles causas podrían ser la interrupción de la conexión con la BBDD o múltiples redenciones afectadas por un capping. Recomendamos, en estos casos, tratar este error y avisar al usuario final.
func cancelCoupon() {
let cancelCouponRequest = EMMAInAppRequest(type: .CancelCoupon)
cancelCouponRequest?.inAppMessageId = "<COUPON_ID>"
EMMA.inAppMessage(cancelCouponRequest)
}
-(void) cancelCoupon {
EMMAInAppRequest * cancelCouponRequest = [[EMMAInAppRequest alloc] initWithType: CancelCoupon];
cancelCouponRequest.inAppMessageId = @"<COUPON_ID>";
[EMMALegacy inAppMessage:cancelCouponRequest];
}
EMMA NativeAd te permite obtener la información de un NativeAd correspondiente a una plantilla que se haya definido y configurado en la plataforma de EMMA.
Obtendremos toda la información del NativeAd disponible para el usuario referente al templateId, según las condiciones que se hayan configurado en la plataforma de EMMA. El objeto EMMANativeAd contiene todos los campos configurados en EMMA para esta plantilla de NativeAd, para obtenerlos se usará el siguiente método:
import UIKit
import EMMA_iOS
class NativeAdExampleViewController: UIViewController, EMMAInAppMessageDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func getNativeAd(templateId: String) {
let nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
EMMA.inAppMessage(nativeAdRequest, with: self)
}
func getBatchNativeAd(templateId: String) {
let nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
nativeAdRequest.isBatch = true
EMMA.inAppMessage(nativeAdRequest, with: self)
}
// MARK: - InAppMessage Delegate
func onReceived(_ nativeAd: EMMANativeAd!) {
let content = nativeAd.nativeAdContent as? [String:AnyObject]
if let title = content?["Title"] as? String) {
print("Received NativeAd with Title: \(title)")
// Draw Native Ad and Send Impression
EMMA.sendImpression(.campaignNativeAd, withId: String(nativeAd.idPromo))
}
}
func onBatchNativeAdReceived(_ nativeAds: [EMMANativeAd]!) {
nativeAds.forEach { (nativeAd) in
if let tag = nativeAd.tag {
print("Received batch nativead with tag: \(tag)")
}
}
}
func openNativeAd(nativeAd: EMMANativeAd) {
// This method executes CTA Action and sends NativeAd Click
EMMA.openNativeAd(String(nativeAd.idPromo))
}
func sendNativeAdClick(nativeAd: EMMANativeAd) {
// Send manual click. Useful if we want to override CTA action
EMMA.sendClick(.campaignNativeAd, withId: String(nativeAd.idPromo))
}
func onShown(_ campaign: EMMACampaign!) {
}
func onHide(_ campaign: EMMACampaign!) {
}
func onClose(_ campaign: EMMACampaign!) {
}
}
// NativeAdExampleViewController.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface NativeAdExampleViewController : UIViewController<EMMAInAppMessageDelegate>
@end
// NativeAdExampleViewController.m
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
#import "NativeAdExampleViewController.h"
@interface NativeAdExampleViewController ()
@end
@implementation NativeAdExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void) getNativeAd: (NSString*) templateId {
EMMANativeAdRequest * nativeAdRequest = [EMMANativeAdRequest new];
nativeAdRequest.templateId = templateId;
[EMMALegacy inAppMessage: nativeAdRequest withDelegate:self];
}
- (void) getBatchNativeAd: (NSString *) templateId {
EMMANativeAdRequest * nativeAdRequest = [EMMANativeAdRequest new];
nativeAdRequest.templateId = templateId;
nativeAdRequest.isBatch = YES;
[EMMALegacy inAppMessage: nativeAdRequest withDelegate:self];
}
- (void) onReceived:(EMMANativeAd *)nativeAd {
NSDictionary * nativeAdContent = [nativeAd nativeAdContent];
NSString * title = [nativeAdContent objectForKey:@"Title"];
if (title) {
NSLog(@"Received NativeAd with title %@", title);
// Show Native Ad
// Send impression
[EMMALegacy sendImpression: kCampaignNativeAd withId:[@(nativeAd.idPromo) stringValue]];
}
}
- (void) openNativeAd: (EMMANativeAd *) nativeAd {
// This method executes CTA Action and sends NativeAd Click
[EMMALegacy openNativeAd: [@(nativeAd.idPromo) stringValue]];
}
- (void) sendNativeAdClick: (EMMANativeAd *) nativeAd {
// Send manual click. Useful if we want to override CTA action
[EMMALegacy sendClick:kCampaignNativeAd withId:[@(nativeAd.idPromo) stringValue]];
}
- (void)onClose:(EMMACampaign *)campaign {
}
- (void)onHide:(EMMACampaign *)campaign {
}
- (void)onShown:(EMMACampaign *)campaign {
}
@end
Se llama a onReceived en el caso de que exista un NativeAd correspondiente a la plantilla con identificador “templateId”.
Se llama a onBatchReceived cuando se realiza la llamada de NativeAd con el parámetro batch a true y existen uno o más NativeAds correspondientes a la plantilla con identificador "templateId".
EMMANativeAd contiene todos los campos configurados en EMMA para esta plantilla de NativeAd, para obtenerlos se usará el siguiente método:
let content = nativeAd.nativeAdContent as? [String:AnyObject]
let title = content?["Title"] as? String
let image = content?["Main picture"] as? String
let cta = content?["CTA"] as? String
printSide(title, image, cta)
En el caso de configurar una plantilla contenedor se pueden obtener los valores del NativeAd de la siguiente forma:
let content = nativeAd.nativeAdContent
if let container = content?["container"] as? Array<[String: String]> {
container.forEach { (containerFields: [String : String]) in
let title = containerFields?["Title"]
let image = containerFields?["Main picture"]
let cta = containerFields?["CTA"]
printSide(title, image, cta)
}
}
Una vez obtenidos todos los campos requeridos, ya se puede crear la vista para pintar este NativeAd en pantalla en función del diseño que se le quiera aplicar. Una vez pintado el NativeAd en pantalla es necesario llamar a este método para poder obtener las impresiones en el reporting:
EMMA.sendImpression(.campaignNativeAd, withId: String(nativeAd.idPromo))
- (void) sendNativeAdClick: (EMMANativeAd *) nativeAd {
[EMMALegacy sendClick:kCampaignNativeAd withId:[@(nativeAd.idPromo) stringValue]];
}
func openNativeAd(nativeAd: EMMANativeAd) {
// This method executes CTA Action and sends NativeAd Click
EMMA.openNativeAd(String(nativeAd.idPromo))
}
- (void) openNativeAd: (EMMANativeAd *) nativeAd {
// This method executes CTA Action and sends NativeAd Click
[EMMALegacy openNativeAd: [@(nativeAd.idPromo) stringValue]];
}
Con esta llamada, se mostrará el contenido del link configurado en el NativeAd desde la plataforma de EMMA. El método openNativeAd internamente envía el event click a EMMA.
Alternativamente, si no se usa este método se puede enviar el click llamando al método:
func sendNativeAdClick(nativeAd: EMMANativeAd) {
// Send manual click. Useful if we want to override CTA action
EMMA.sendClick(.campaignNativeAd, withId: String(nativeAd.idPromo))
}
- (void) sendNativeAdClick: (EMMANativeAd *) nativeAd {
// Send manual click. Useful if we want to override CTA action
[EMMALegacy sendClick:kCampaignNativeAd withId:[@(nativeAd.idPromo) stringValue]];
}
Para NativeAd múltiple se utilizará una subclase de EMMAInAppRequest, EMMANativeAdRequests. Hasta ahora el parámetro template ID iba en la clase EMMAInAppRequest, ahora quedará deprecated en esta y pasará a ser parámetro de la subclase EMMANativeAdRequest. Se podrá usar de ambas formas, pero la forma correcta será en la subclase.
func getBatchNativeAd(templateId: String) {
let nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
nativeAdRequest.isBatch = true
EMMA.inAppMessage(nativeAdRequest, with: self)
}
- (void) getBatchNativeAd: (NSString *) templateId {
EMMANativeAdRequest * nativeAdRequest = [EMMANativeAdRequest new];
nativeAdRequest.templateId = templateId;
nativeAdRequest.isBatch = YES;
[EMMALegacy inAppMessage: nativeAdRequest withDelegate:self];
}
func getNativeAd(templateId: String) {
let nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
EMMA.inAppMessage(nativeAdRequest, with: self)
}
- (void) getNativeAd: (NSString*) templateId {
EMMANativeAdRequest * nativeAdRequest = [EMMANativeAdRequest new];
nativeAdRequest.templateId = templateId;
[EMMALegacy inAppMessage: nativeAdRequest withDelegate:self];
}
A partir de la versión 4.9.0 se ha añadido la posibilidad de poder añadir plugins in-app al SDK. Los plugins in-app funcionan a través de la tecnología de Native Ad.
Puedes crear tu propio formato de comunicación y convertirlo en un plugin in-app, para ello es necesario que la clase principal del nuevo formato extienda de la clase abstracta EMMAInAppPlugin, esta clase obliga a sobrescribir dos métodos:
import EMMA_iOS
public class CustomInAppPlugin: EMMAInAppPlugin {
public func getId() -> String {
return "emma-plugin-custom"
}
public func show(_ nativeAd: EMMANativeAd) {
// Process data
}
public func dismiss() {
}
}
// CustomInAppPlugin.h
#import <UIKit/UIKit.h>
#import "EMMA_iOS/EMMA_iOS.h"
@interface CustomInAppPlugin: NSObject<EMMAInAppPluginProtocol>
@end
// CustomInAppPlugin.m
#import <Foundation/Foundation.h>
#import "CustomInAppPlugin.h"
@implementation CustomInAppPlugin
- (void)dismiss {
}
- (NSString * _Nonnull)getId {
return @"emma-plugin-custom";
}
- (void)show:(EMMANativeAd * _Nonnull)nativeAd {
// Process data
}
@end
El método show() es el método princiapal ya que es el que lanza el SDK cuando recibe el Native Ad correspondiente al plugin, el SDK pasa al plugin el Native Ad con el contenido acorde a la plantilla marcada como plugin en el Dashboard. Con estos parámetros se puede crear cualquier formato de comunicación adaptado al contenido del Native Ad.
El método dismiss() es para ocultar el plugin automáticamente, de momento el SDK no tiene incorporado esta funcionalidad, deja el control de ocultado al propio plugin.
El método getId() devuelve el identificador del plugin que corresponde con el templateId generado en la plantilla.
La clase EMMAInAppPlugin contiene varios métodos estáticos cómo sendInAppImpression y sendInAppClick,al igual que en Native Ad, puedes enviar estas acciones. También puedes invocar al/los inappMessageListener con los métodos invokeShownListeners, invokeCloseListeners y invokeHideListeners.
Puedes consultar el ejemplo de plugin aquí
Para integrar un plugin es necesario añadirlo en el sdk después del inicio de sesión, para ello es necesario utilizar el método addInAppPlugin.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
....
EMMA.startSession(with: configuration)
EMMA.addInAppPlugin([CustomInAppPlugin()])
return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[EMMALegacy startSessionWithConfiguration:configuration];
....
[EMMALegacy addInAppPlugins:@[[CustomInAppPlugin new]]];
return YES;
}
Una vez el sdk tenga el plugin simplemente hay que llamarlo en la parte de la app donde se requiera igual que si de un Native Ad se tratase.
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = "emma-plugin-custom"
EMMA.inAppMessage(request: nativeAdRequest)
}
}
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
EMMANativeAdRequest * nativeAdRequest = [EMMANativeAdRequest new];
nativeAdRequest.templateId = @"emma-plugin-custom";
[EMMALegacy inAppMessage: nativeAdRequest withDelegate:self];
}
@end
Con esta funcionalidad podemos limitar las urls que abrirá el SDK de EMMA , de modo que en las comunicaciones In-App, sólo se mostrarán en un Webview aquel contenido que empiece por alguna de las urls que hayamos indicado en la whitelist.
Si no indicamos ninguna url en la whitelist, cualquier url está permitida.
Esta funcionalidad afectaría a los Push (Rich URL), Banners, StartViews, AdBalls y DynamicTabs.
Las comunicaciones (Banners, StartViews, AdBalls y DynamicTabs) pueden cargar contenido externo a la app mediante un Webview y para los Banners, AdBalls y DynamicTabs podrían cargar imágenes externas que también serían controladas por la whitelist.
Para un Push con Rich URL, si ésta no cumple, no se abriría la Webview correspondiente, pero el push sí que llega a la aplicación, hay que tener en cuenta que si en vez de una url se emplea un deeplink, el scheme del deeplink debe ser añadido a la whitelist para poder abrirlo.
Debemos llamar a este método después del starteMMaSession y antes de llamar a cualquier método relativo a las comunicaciones In-App.
func enableWhitelist() {
// If set the SDK only allows communication with following domains
EMMA.setWhitelist(["https://www.emma.io", "https://www.emmasolutions.net"])
}
- (void) enableWhitelist {
// If set the SDK only allows communication with following domains
[EMMALegacy setWhitelist(@[@"https://www.emma.io", @"https://www.emmasolutions.net"]];
}
Si mi whitelist es “http://mydomain.com”.
Subimos al dashboard de EMMA un Banner con Target URL https://mydomain.com
El Banner no se mostrará, deberíamos añadir a la whitelist https://mydomain.com
Configuramos en el dashboard de EMMA una StartView con StartView URL http://www.mydomain.com
La StartView no se mostrará, deberíamos añadir a la whitelist http://www.mydomain.com
Configuramos en el dashboard de EMMA un Banner con Target URL http://mydomain.com/my/url y SmartPhone Banner URL http://subdomain.mydomain.com/my/image
El Banner no se mostrará, la url de la imagen no cumple con la whitelist, deberíamos añadir a la whitelist http://subdomain.mydomain.com
Subimos al dashboard de EMMA un Banner con Target URL http://mydomain.com/my/url/
El Banner se mostrará porque la url introducida en el campo Target URL empieza por el mismo protocolo y dominio que la url de la whitelist
Configuramos en el dashboard de EMMA una StartView con StartView URL http://mydomain.com/mypage.html¶m=value
La StartView se mostrará porque la url introducida en el campo StartView URL empieza por el mismo protocolo y dominio que la url de la whitelist
Si lo deseas puedes pasar un custom String que etiquete la Campaña en caso de que utilices más de una Campaña del mismo tipo en tu app y necesites distinguirlas.
let startViewinAppRequest = EMMAInAppRequest(type: .Startview)
// Optional. You can filter by label
startViewinAppRequest?.label = "<LABEL>"
Apple anunció que con iOS 14 se necesitaría el permiso explícito de los usuarios para rastrearlos o acceder al Identificador de anunciantes (IDFA). Eso se tradujo en que para vincular los datos del usuario o dispositivo recopilados deberás solicitar permiso a través del marco de AppTrackingTransparency. Para solicitar el permiso en la aplicación usa el siguiente método:
if #available(iOS 14.0, *) {
EMMA.requestTrackingWithIdfa()
}
if (@available(iOS 14, *)) {
[EMMALegacy requestTrackingWithIdfa];
}
EMMA permite inhabilitar el trackeo o seguimiento para aquellos usuarios que manifiesten este deseo.
Este método es la mejor forma de adaptarse a la nueva RGPD (Reglamento General de la Protección de Datos).
La comunicación del usuario para expresar esta opción de no seguimiento debe ser tratada por la app haciendo una llamada al siguiente método:
public func disableUserTracking(deleteUserData: Bool) {
/*
EMMA.disableUserTracking shuts down all communication with EMMA servers
If deleteUserData is true; EMMA will remove this device data from
their servers. This is a unrecoverable action.
*/
EMMA.disableUserTracking(deleteUserData)
}
- (void) disableUserTracking: (BOOL) deleteUserData {
/*
EMMA.disableUserTracking shuts down all communication with EMMA servers
If deleteUserData is true; EMMA will remove this device data from
their servers. This is a unrecoverable action.
*/
[EMMALegacy disableUserTracking: deleteUserData];
}
En el caso de que se quiera volver a activar las comunicaciones del usuario, se puede utilizar este otro método:
public func enableUserTracking() {
EMMA.enableUserTracking()
}
- (void) enableUserTracking {
[EMMALegacy enableUserTracking];
}