You can add the EMMA SDK with SPM starting from version 4.9.0+
and requires Xcode 12 or higher.
Example steps for integration:
File > Add Package Dependencies...
https://github.com/EMMADevelopment/eMMa-iOS-SDK
.4.12.0
. This ensures that you always use the latest versions within the 4.x
range.The latest version of the SDK is 4.15.4. See this page for details on updates.
The latest version of EMMA is available via CocoaPods:
gem install cocoapods
.pod setup
to download the repository with all the "specs" and check them locally, this will directly create a copy of the GitHub repository.Podfile
.pod 'eMMa'
.pod install
.Once done, CocoaPods will download and install the EMMA library by creating a new .xcworkspace
file. Finally, open this file in Xcode.
To download manually follow the steps below:
EMMA_iOS.xcframework
from the following link.EMMA_iOS.xcframework
to your Xcode project in the Frameworks, Libraries,and Embedded Content
section.Embed and Sign
.EMMA_iOS.xcframework
to Framework and Libraries
and select Do Not Embed
.NOTE: This is the minimum requirement to start measuring installations of your App.
Obtain EMMA's Session Key
.
See the documentation to see where you can get your EMMA Key
at the EMMA documentation page.
The place where it makes more sense to initialize the EMMA library is in the application(_:didFinishLaunchingWithOptions:) method.
First we must import the EMMA module import EMMA_iOS
and call the method EMMA.startSession(with:)
with the data of your Session Key
.
Example of basic integration:
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
Screen sending is active by default in the EMMA SDK. To disable it use the following configuration:
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
If you are upgrading from a previous version of the SDK, check this page first for possible implementation changes.
Documentation for integrating the ability to manage acquisition campaigns:
SKAdNetwork (SKAN) is a tool released by Apple that allows advertisers to measure campaign performance without violating user privacy. This means that identifiers with IDFA or IDFV are not used to track the user, nor does it require the consent of the user in the ATT.
To implement this functionality it is necessary to integrate SDK version 4.12.0 or higher.
Since iOS 15 Apple allows you to send these copies of postbacks, not only to advertisers as it has been doing up to now, but to any endpoint previously added in the app's configuration.
How does the SKAdNetwork work? First, the advertiser shows an ad from an app (Advertiser app) in its own application or in a third-party application (Publisher app). The user views the ad, clicks on it, and the advertised app is downloaded from the AppStore. The user then opens the app, the EMMA SDK notifies SKAdNetwork of the app opening in the EMMA.startSession(with:)
method, and Apple sends a copy of the postback to our domain added in the info.plist
.
For SKAdNetwork 3.0 and below, the postback is sent after the attribution window which is from 0 to 24 hours. It is not possible to know exactly when it is sent since Apple does it randomly in the 24 or 48 hours after the end of the 24-hour window.
For SKAdNetwork 4.0, up to 3 postbacks are sent after each attribution window. The delivery is made 24h-48h later for the first postback and 24h-144h for the second and third postbacks. In this case, it is not possible to predict the shipping time either, since it does so randomly within the windows. In the following image you can see the window of each postback and its corresponding delay.
To register the EMMA domain in the info.plist
of the app, follow these steps:
Add the NSAdvertisingAttributionReportEndpoint
key to the info.plist
of the app (for details see Apple documentation).
Add https://emma-skadnetwork.com
as the value of the key. It is important that the value has exactly this format.
Once the domain is added to the info.plist
, make sure that the app contains the StoreKit.framework
. The StoreKit.framework
is the framework that handles communication with SKAdNetwork starting with iOS 14+. To make sure the app works correctly add StoreKit.framework
to the app as Optional.
How can I test the integration? It is true that SKAdNetwork cannot be tested in a real way in test or pre-production environments (Testflight) but the EMMA SDK can give us clues of its correct implementation in these environments. To make sure that this functionality is well integrated, follow these steps:
startSession
method in the AppDelegate
, you see the following log: EMMALogger: 4 - Registering app for SKAdNetwork
.SKAdNetwork: Error while updating conversion value: Error Domain=SKANErrorDomain Code=10 "(null)"
. This error is normal in test environments as the SKAdNetwork does not work, but it gives us a clue that the integration is correct and that the StoreKit.framework
is working. Any other error or log that the SKAdNetwork API is not available is a symptom of an integration problem with StoreKit.framework
.Set up sub-domain for powlink.
Powlink for iOS uses Apple's Universal Links technology. In order to have direct application openings via universal links, we will need to set up a unique domain or subdomain. See the Powlink configuration support guide on the EMMA dashboard.
Add Universal Links support to your application.
See the Apple guide to add Universal Links to the application.
For the correct operation of the powlink, EMMA must know all the links that are opened in the application externally, either from an external URL, or from a deeplink implemented in the application.
To do this you must implement the method application(_:continue:restorationHandler:) in your AppDelegate
and execute the method EMMA.handleLink(url: URL)
.
By processing the paths of the received Powlinks you will be able to launch different parts of your application.
Example of implementation of the EMMA.handleLink(url: URL)
method:
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;
}
If you are using a tracker with a domain other than EMMA's (.powlink.io or .pwlnk.io), it is necessary to add the domain when starting the library so that this domain is recognized as a domain capable of attributing campaigns.
/*
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"]
With EMMA you can perform a complete SDK integration that allows you to know the location of your users, how they register in your App, how many transactions they perform and even their own characteristics. That is, all the information about your users that you will get in the Behavior section.
The EMMA platform differentiates between two types of events. Those that the platform includes by default and the Custom events that you want to integrate according to the structure of your application.
You can find more information about default events here.
Follow the next steps to integrate them:
The method EMMA.registerUser(userId:forMail:andExtras:)
allows to send information about the registrations in the application.
Example
func register() {
EMMA.registerUser("554234", forMail: "test@emma.io")
}
- (void) registerUser {
[EMMALegacy registerUser:@"554234" forMail:@"test@emma.io"];
}
The method EMMA.loginUser(userId:forMail:andExtras:)
allows to send information about login
events.
If we have a successive login
event with the same data, we can use the method EMMA.loginDefault()
. This method would be useful in the case of an "Auto-Login", for example.
func login() {
EMMA.loginUser("554234", forMail: "test@emma.io")
}
- (void) registerUser {
[EMMALegacy loginUser:@"554234" forMail:@"test@emma.io"];
}
Use EMMA.trackEvent(request:)
to count the number of times certain events happen during a session in your app.
This can be useful to measure how many times users convert to different actions, for example.
You can obtain event tokens by creating them on the EMMA platform. If a non-existing token is sent to EMMA, an error will be returned.
For more information, click here.
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(request: 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];
For the event request a delegate can be added to control the status of the request and whether it returns data (e.g. a rule) or not.
Example:
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 allows you to measure any transaction or purchase made in your app. This is an example to measure a transaction:
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];
}
The method to start the transaction is 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:@""];
Once the transaction is started we have to add the products to the transaction. To do this we will use the method 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];
Once we have all the products added, we execute the transaction measurement with the EMMA.trackOrder()
method.
EMMA.trackOrder()
[EMMALegacy trackOrder];
In case we need to cancel the tracking of a transaction we will use the method EMMA.cancelOrder(orderId:)
.
func cancelTransaction() {
EMMA.cancelOrder("<ORDER_ID>")
}
-(void) cancelTransaction {
[EMMALegacy cancelOrder:@"<ORDER_ID>"];
}
The method EMMA.trackExtraUserInfo(info:)
updates or adds extra parameters in order to get better segmentation in the Users with tag
filtering. It can be used in the registration, login or any other section of the app where user information is collected.
If you want to use the EMMA RULE
On his Birthday
, send the birthday date with the method for the TAG in the following format (ISO):
Name:BIRTHDAY
Value:YYYYYY-MM-DD
Remember to check the SDK logs for the list of TAGS that cannot be used because they are reserved for the EMMA system.
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 can attach the user's current location, if the application has permissions with the EMMA.trackLocation()
method.
We can retrieve the EMMA user ID with the method EMMA.getUserId(resultBlock:)
.
This method returns the EMMA ID as a String. This ID is unique for each user and can be used to filter when communications are sent.
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);
}
}]
}
The identifier format is UUID V4 type. To obtain the device identifier use the following method:
EMMA.deviceId()
[EMMALegacy deviceId];
To send the customer ID independently of the login/registration use the following method:
EMMA.setCustomerId(customerId: "<Customer ID>")
[EMMALegacy setCustomerId:@"<Customer ID>"];
Manually sets the user's preferred language.
This method allows overwriting the default language of the device to set a custom language to be used in all SDK requests. This is useful in applications that allow the user to select a different language than the one configured on the device.
The language code in ISO 639-1 format must be used: es
(Spanish), en
(English), fr
(French), de
(German), it
(Italian), zh-Hans
(Simplified Chinese), zh-Hant
(Traditional Chinese), etc.
// In this case, it establishes English as the language
EMMA.setUserLanguage("en")
// In this case, it establishes English as the language
[EMMALegacy setUserLanguage:@"en"];
If this method is not called, EMMA will default to the user's preferred language configured in the device system.
The method EMMA.getUserInfo(resultBlock:)
retrieves the profile of the user we have registered in EMMA.
The data returned by the call is obtained from the information collected by the SDK itself. On a first start it may take a few seconds to collect the information, if the call is made just after the
startSession
it may return nil.
User profile fields reference.
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);
}
}]
}
Through the method EMMA.installAttributionInfo(attributionDelegate:)
we will be able to get the installation attribution data for each user.
See the description of attribution fields for the available information.
As of version 4.6.2 the campaign receives the click parameters through the clickParams
method.
In order to get information about the attribution of the installation, the following example can be followed:
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
To differentiate a notification from our push system with respect to other systems, the payload sent by EMMA contains a flag called "eMMa".
EMMA allows you to add a powerful Push Notification system that is easy to integrate. The platform also allows you to send info through notifications and generate any action within your app thanks to them.
The Apple APNs Auth Key is a type of certificate (.p8) that replaces its predecessors: the production APNs certificate and the development APNs certificate. EMMA recommends the use of this certificate for the following reasons:
To use this certificate it is necessary:
How to obtain the Push Auth Key in Apple.
Once you have generated the certificates for your app, you can start integrating Push Notifications. Below is an example of an AppDelegate complete with push integration:
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, EMMAPushDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
//Enable EMMA Push System
EMMA.startPushSystem()
EMMA.setPushSystemDelegate(self)
}
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);
}
}
To maintain the badge values between the extension and the application, it is necessary to add a new AppGroup to the app's Capabilities with the following format: group.YOUR_BUNDLE_ID.emma
Optionally, if you want to control what is received from a Push, your AppDelegate
can implement the EMMAPushDelegate
delegate and the onPushOpen
method that will be called when the user opens the notification.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, EMMAPushDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ...
configuration.pushDelegate = self
// ...
}
func onPushOpen(_ push: EMMAPush) {
// Do whatever you want with push
}
}
To register the token it is necessary to activate Push Notifications in Capabilities.
To reset the badge that counts the number of notifications received, there are two ways:
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];
The badge is saved in the user's preferences. To synchronize these preferences between the app and the extension, it is necessary to configure an AppGroup in "Capabilities", both in the target of the App and in the extension. The format of the bundle must be "group.{bundleId}.emma", where bundleId is the Bundle ID of the target App.
Rich Push Notifications are enabled via a Notification Service Extension, a separate binary within your application package. Before displaying a new push notification, the system will call your Notification Service Extension allowing you to modify the content and attachments that will be displayed.
To create the Notification Service Extension in your project, in Xcode choose File -> New -> Target
and choose the Notification Service Extension template.
You can name the extension as you like, we will call it RichPushExtension for this guide. Be sure to include the extension in your application.
When you click Finish you will be asked to activate the extension, click Activate to finish.
At the end of these steps, three new files have been added to the directory with the name of the extension we have created (RichPushExtension in our case): NotificationService.h
, NotificationService.m
and Info.plist
.
Finally make sure that push notifications are enabled for the extension we have created. Select your new extension (RichPushExtension), choose Capabilities and enable 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
To add the SDK to the extension it is necessary to modify the Podfile file by adding the eMMa pod to the extension target:
target 'ProjectName' do
pod 'eMMa', '~> 4.10.1'
end
target 'EMMANotificationServiceExtension' do
pod 'eMMa', '~> 4.10.1'
end
To use custom sounds in the notifications you send with EMMA, you have to add the .caf sound files you want to your application, in the root of your application package or in Library/Sounds/. Remember to use the same file names for the sounds on iOS and Android.
You can redirect push notification opens to a section in your app. To do this you should use a structure like this:
scheme://host/page1/page2/page3
On SDK version 4.10.x, functionality of buttons with actions in notifications has been added. In order to track the action from which the notification is opened, we must replace the handlePush
that we are using so far with this one:
@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();
}
To ensure correct operation, check that the SDK that contains the notification service has version 4.10.x and is aligned with the version of the app.
See here to configure a push with these actions.
EMMA includes 8 different communication formats that you can integrate to impact your users on iOS:
EMMA NativeAd allows you to get the information of a NativeAd corresponding to a template that has been defined and configured in the EMMA platform.
We will obtain all the NativeAd information available to the user regarding the templateId
, according to the conditions that have been configured on the EMMA platform. The EMMANativeAd
object contains all the fields configured in EMMA for this NativeAd template, to obtain them the following method will be used:
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
onReceived is called if there is a NativeAd corresponding to the template with identifier “templateId”.
onBatchReceived is called when the NativeAd call is made with the batch parameter set to true and one or more NativeAds corresponding to the template with identifier "templateId" exist.
EMMANativeAd
contains all the fields configured in EMMA for this NativeAd template, to obtain them the following method will be used:
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)
In the case of configuring a container template, the NativeAd values can be obtained as follows:
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)
}
}
Once all the required fields have been obtained, the view can be created to paint this NativeAd on the screen depending on the design that you want to apply to it. Once the NativeAd has been painted on the screen, it is necessary to call this method to obtain the impressions in the 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]];
}
With this call, the content of the link configured in the NativeAd will be displayed from the EMMA platform. The openNativeAd
method internally sends the event click to EMMA.
Alternatively, if this method is not used, the click can be sent by calling the method:
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]];
}
For NativeAd multiple we will use a class of EMMAInAppRequest
or a subclass of EMMANativeAdRequests
. Until now the templateId
parameter was in the EMMAInAppRequest
class, now it will be deprecated and will become a parameter of the EMMANativeAdRequest
subclass. It can be used in both ways, but the correct way will be in the subclass.
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];
}
EMMA's StartView allows you to display HTML information at the top of your WebView.
To enable the use of StartViews you must integrate:
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 allows you to display a small pie chart in your application that the user can scroll freely on the home screen of the app and also close it. Clicking on it, displays a pop-up with HTML content.
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];
}
Use to check if the AdBall is being displayed on the device screen. Returns BOOL
true
if it is being displayed on the screen.
//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");
}
EMMA Banners allow you to display a banner in your app with customized promotional information. The banner will be displayed depending on the configuration you set in the EMMA dashboard. It is a communication that allows you to show the user an external WebView with HTML content or redirect to another tab of the application (via deeplink).
To display the Banners in your app you need to at least integrate the first method described below:
It is recommended to make the call inside the
viewDidLayoutSubviews
function.
func getBanner() {
let bannerRequest = EMMAInAppRequest(type: .Banner)
EMMA.inAppMessage(bannerRequest)
}
-(void) getBanner {
EMMAInAppRequest *bannerRequest = [[EMMAInAppRequest alloc] initWithType:Banner];
[EMMALegacy inAppMessage:bannerRequest];
}
The EMMA Strip allows you to display a text banner over where the Status Bar is usually placed with a message to your users. This can be displayed on any screen of the app.
func getStrip() {
let stripRequest = EMMAInAppRequest(type: .Strip)
EMMA.inAppMessage(stripRequest)
}
-(void) getStrip {
EMMAInAppRequest * stripRequest = [[EMMAInAppRequest alloc] initWithType: Strip];
[EMMALegacy inAppMessage:stripRequest];
}
EMMA Coupons allows you to obtain, verify and redeem coupons that have been defined and configured in the EMMA platform.
A list of the existing coupons will be returned, listing first the automatic coupons ordered from most recent to oldest and then the classic coupons will be listed, also ordered from most recent to oldest.
In the response block, we will obtain a dictionary with the information related to each coupon available to the user. For each coupon the following information is available: id (EMMA internal identifier), code, maximum number of redemptions, number of times redeemed, title, description, image, etc.
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
With this call, we will obtain the information related to a specific coupon.
The couponId
parameter must be the internal EMMA identifier of a coupon, identifier that can be obtained from a previous call to Coupons.
In the response block, we will obtain a dictionary with the information related to the consulted coupon: id (EMMA internal identifier), code, maximum number of redemptions, number of times redeemed, title, description, image, etc.
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]];
}
}
With this call we can check if the user can redeem the indicated coupon.
The couponId
parameter must be the internal EMMA identifier of a coupon, identifier that can be obtained from a call to checkForCoupons
made previously.
In the block response, it will indicate the number of times the user can still redeem the 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);
}
}
With this call, the user redeems the indicated coupon.
The couponId
parameter must be the internal EMMA identifier of a coupon, an identifier that can be obtained from a previously made Coupons call.
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];
}
With this call it is possible to cancel the redemption of a coupon previously made.
The couponId
parameter must be the EMMA internal identifier of a coupon identifier that can be obtained in the Coupons type call.
Optionally you can specify a count
parameter if you want to cancel more than one previously performed redemption. If not specified, the coupon will be canceled once.
Error 511 reports that the redeem could not be completed for some reason. Some possible causes could be the interruption of the connection with the DB or multiple redemptions affected by capping. We recommend, in these cases, to handle this error and notify the end user.
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];
}
The EMMA TabBar View (DynamicTab) allows you to display HTML information in a new section of your application (only in case you already use a TabBar in your app).
The new TabBar Item can be created locally or in the EMMA platform. If both ways are used, the dynamic TabBar Item created directly in EMMA will prevail.
To enable this possibility in your app, you will need to implement at least the first method. It can be configured by following these instructions:
The TabBar item index and the item itself can be specified in the EMMA platform. As indicated above, the configuration in EMMA will always override the local configuration.
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];
}
As of version 4.9.0 the possibility to add in-app plugins to the SDK has been added. In-app plugins work through NativeAd technology.
You can create your own communication format and turn it into an in-app plugin, for this it is necessary that the main class of the new format extends the abstract class EMMAInAppPlugin
, this class forces to override two methods:
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
The show()
method is the main method because it is the one that launches the SDK when it receives the NativeAd corresponding to the plugin, the SDK passes to the plugin the NativeAd with the content according to the template marked as a plugin in the Dashboard. With these parameters you can create any communication format adapted to the content of the NativeAd.
The dismiss()
method is to hide the plugin automatically. At the moment, the SDK does not have this functionality built in, it leaves the control of hiding to the plugin itself.
The getId()
method returns the plugin identifier that corresponds to the templateId
generated in the template.
The EMMAInAppPlugin
class contains several static methods such as sendInAppImpression
and sendInAppClick
. As in NativeAd, you can send these actions. You can also invoke the inappMessageListener
with the invokeShownListeners
, invokeCloseListeners
and invokeHideListeners
methods.
You can check the plugin example here.
To integrate a plugin it is necessary to add it in the SDK after the login, for this it is necessary to use the addInAppPlugin
method.
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;
}
Once the SDK has the plugin you simply have to call it in the part of the app where it is required as if it were a NativeAd.
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
Below is a complete example of how to integrate In-App campaigns with 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
With this functionality we can limit the URLs that the EMMA SDK will open, so that in the In-App communications, only the content that begins with one of the URLs that we have indicated in the whitelist will be shown in a Webview.
If we do not indicate any URL in the whitelist, any URL is allowed.
This functionality would affect Push (Rich URL), Banners, StartViews, AdBalls and DynamicTabs.
Communications (Banners, StartViews, AdBalls and DynamicTabs) can load external content to the app through a Webview and for Banners, AdBalls and DynamicTabs they could load external images that would also be controlled by the whitelist.
For a Push with Rich URL, if it does not comply, the corresponding Webview would not open, but the push does reach the app, note that if instead of a URL a deeplink is used, the deeplink scheme must be added to the whitelist to be able to open it.
This method must be called after the startSession
and before calling any method related to In-App communications.
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"]];
}
If my whitelist is "http://mydomain.com".
Upload to EMMA dashboard a Banner with Target URL https://mydomain.com.
The Banner will not be displayed, we should add https://mydomain.com to the whitelist.
Configure a StartView with StartView URL http://www.mydomain.com in the EMMA dashboard.
The StartView will not be displayed, we should add http://www.mydomain.com to the whitelist.
Configure in the EMMA dashboard a Banner with Target URL http://mydomain.com/my/url and SmartPhone Banner URL http://subdomain.mydomain.com/my/image.
The Banner will not be displayed, the URL of the image does not comply with the whitelist, we should add to the whitelist http://subdomain.mydomain.com.
We upload a Banner with Target URL http://mydomain.com/my/url/ to the EMMA dashboard.
The Banner will be displayed because the URL entered in the Target URL field starts with the same protocol and domain as the whitelist URL.
We configure in the EMMA dashboard a StartView with StartView URL http://mydomain.com/mypage.html¶m=value.
The StartView will be displayed because the URL entered in the StartView URL field starts with the same protocol and domain as the whitelist URL.
If you want, you can pass a custom String that labels the Campaign in case you use more than one Campaign of the same type in your app and you need to distinguish them.
let startViewinAppRequest = EMMAInAppRequest(type: .Startview)
// Optional. You can filter by label
startViewinAppRequest?.label = "<LABEL>"
Before proceeding, it is necessary to add the parameters to the communication in the EMMA dashboard so that the SDK picks them up and makes them available to the application. To learn more.
We can get the campaign parameters once we have received the NativeAd, either in the onReceived
method or in onBatchNativeAdReceived
. To learn more about its implementation.
Example of use with onReceived
:
class NativeAdViewController : UIViewController, EMMAInAppMessageDelegate {
// ...
func onReceived(_ nativeAd: EMMANativeAd!) {
print(nativeAd.params)
}
}
For the StartView, AdBall, Strip and Banner formats, the campaign parameters are obtained through the methods provided by the EMMAInAppMessageDelegate
: onShown
, onHide
and onClose
.
Each of these methods receives as parameter an instance of EMMACampaign
, through which it is possible to access the campaign parameters through the campaign.params
property.
Usage example:
// EMMAInAppMessageDelegate implementation
class HomeViewController: UIViewController, EMMAInAppMessageDelegate {
// ...
func onShown(_ campaign: EMMACampaign!) {
if (campaign != nil && campaign.params != nil && campaign.params.count > 0) {
print(campaign.params)
}
}
func onClose(_ campaign: EMMACampaign!) {
// Access to campaign.params if necessary
}
func onHide(_ campaign: EMMACampaign!) {
// Access to campaign.params if necessary
}
}
Apple announced that with iOS 14 you would need explicit permission from users to track them or access the Advertiser Identifier (IDFA). That translated to mean that in order to link collected user or device data you will need to request permission through the AppTrackingTransparency framework. To request permission in the app use the following method:
if #available(iOS 14.0, *) {
EMMA.requestTrackingWithIdfa()
}
if (@available(iOS 14, *)) {
[EMMALegacy requestTrackingWithIdfa];
}
Remember to include a
NSUserTrackingUsageDescription
key in theInfo.plist
file. This message should clearly explain why your application is requesting permission to track user activity, as this is a requirement to comply with Apple's privacy policies. For more information, please refer to Apple's official documentation.
EMMA allows you to disable tracking for those users who express this wish.
This method is the best way to adapt to the new RGPD (General Data Protection Regulation).
The user's communication to express this do-not-track option must be handled by the app by making a call to the following method:
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];
}
In case you want to re-enable the user's communications, you can use this other method:
public func enableUserTracking() {
EMMA.enableUserTracking()
}
- (void) enableUserTracking {
[EMMALegacy enableUserTracking];
}