IOS integration manual

Instructions

Library is distributed as static library written in Objective-C. Library consist also Bundle file which contains information needed for CoreData. Import the library and bundle file by dragging the to project window.

Complete required plist file configuration:

      
    <key>amAppId</key>
    <string><<<APPLICATION ID>>></string>
    <key>amSyncMeantime</key>
    <string>60</string>
    <key>amDevice~ipad</key>
    <string>TABLET</string>
    <key>amDevice~iphone</key>
    <string>PHONE</string>
    <key>amDevice~ipod</key>
    <string>PHONE</string>
    <key>amSessionSplitTimeInterval</key>
    <string>5</string>
    <key>amVendorId</key>
    <string><<<VENDOR ID>>></string>
    <key>NSAppTransportSecurity</key>
      <dict>
          <key>NSExceptionDomains</key>
          <dict>
              <key>appmanago.com</key>
              <dict>
                  <key>NSExceptionRequiresForwardSecrecy</key>
                  <false/>
                  <key>NSIncludesSubdomains</key>
                  <true/>
              </dict>
          </dict>
      </dict>
    <key>UIBackgroundModes</key>
    <array>
      <string>remote-notification</string>
    </array>
    

Parameters description:

amAppId– App’s id copied from APPmanago dashboard, leter case is important;
Next three parameters remain the same.

amSessionSplitTimeInterval – time to confirmation of app launch (in minutes). For value=30, the next app usage will be counted only if it will be launched after 30 minutes when it doesn’t run.

amVendorId – copied form APPmanago’s “id vendor” tab;
the rest reminds the same.

amSyncMeantime – time (seconds) between library events and APPmanago server synchronization. Minimum = 60s. Everyvalue from range 1-59 will convert to 60.

AppDelegate implementation

For a library to work correctly, it’s necessary to accomplish methods in AppDelegate.
AppDelegate calls:

The didFinishLaunchingWithOptions method handles notification attendance when app isn’t running. In example, notification attendance is copied in didFinishLaunchingWithOptions and didReceiveRemoteNotification to keep it simple, they vary in completionHandler triggering in the method.
didReceiveRemoteNotification is necessary for iOS. Notification handling may vary depending on the method, or it can be extracted in a more clean way.

  • dialogHandler
    For readability we present extracted dialogHandler. This code is also placed in AppDelegate beside other methods.

        void (^dialogHandler)(AMNotification *) = ^(AMNotification *notification) {
           UIAlertAction *okAction = [UIAlertAction actionWithTitle:notification.payload[@"dialogOk"] style:UIAlertActionStyleDefault
        handler:^(UIAlertAction *action) {
          [[AMMonitor sharedInstance] trackNotificationCallback:notification]; 
        }];
           [[AMMonitor sharedInstance] displayDialog:notification withOkAction:okAction];
        };
        
  • didFinishLaunchingWithOptions
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
           // Override point for customization after application launch.
        
        
           [[AMMonitor sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
        
        
        if (launchOptions) {
           [[AMMonitor sharedInstance] loadPayloadForNotification:launchOptions
                                                   andApplication:application
                                   loadCompletionHandlerWithError:^(AMNotification *notification, NSError *error) {
        if (error) {
        NSLog(@"Error occured while downloading notification :  %@", [error localizedDescription]);
        return;
        }
         // implement your own logic or use default
        [[AMMonitor sharedInstance] handleNotification:notification
                                          notificationHandler:nil
                                                dialogHandler:dialogHandler
                                                   urlHandler:nil
                                                 inAppHandler:nil];
                                   }];
        }
        
        
        
        
        //only for iOS > 8 (implentation for previous version omitted)
           UIUserNotificationType types = UIUserNotificationTypeBadge |
                   UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
           UIUserNotificationSettings *mySettings =
                   [UIUserNotificationSettings settingsForTypes:types categories:nil];
           [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
           [[UIApplication sharedApplication] registerForRemoteNotifications];
        
        
        
        
           return YES;
        }
        
  • didReceiveRemoteNotification:
        - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
        
        
        BOOL amPush = [[AMMonitor sharedInstance] loadPayloadForNotification:userInfo
                                                             andApplication:application loadCompletionHandlerWithError:^(AMNotification *notification, NSError *error) {
         if (error) {
          NSLog(@"Error occured while downloading notification :  %@", [error localizedDescription]);
        completionHandler(UIBackgroundFetchResultNoData);
        return;
        } else {
        completionHandler(UIBackgroundFetchResultNewData);
        }
                                                 // implement your own logic or use default
        [[AMMonitor sharedInstance] handleNotification:notification notificationHandler:nil dialogHandler:dialogHandler urlHandler:nil inAppHandler:nil]; }];
        
        
        if (!amPush) {
           // handle not appmanago notifications here
           completionHandler(UIBackgroundFetchResultNewData);
        }
        }
        
  • didRegisterForRemoteNotificationsWithDeviceToken :

        - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
           [[AMMonitor sharedInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
        }
        
        
  • applicationDidEnterBackground:

        - (void)applicationDidEnterBackground:(UIApplication *)application {
           [[AMMonitor sharedInstance] applicationDidEnterBackground:application];
        }
        
  • applicationWillEnterForeground :

        - (void)applicationWillEnterForeground:(UIApplication *)application {
           [[AMMonitor sharedInstance] applicationWillEnterForeground:application];
        }
        

Handling notification :

    - (BOOL) loadPayloadForNotification:(NSDictionary *)userInfo andApplication:(UIApplication *)application loadCompletionHandlerWithError:(AMNotificationHandlerWithError)completionHandler;
    

The method downloads payload from Appmanago server and allows to handle it in any way. The library provides helper method for default notificaton actions:

    - (void)handleNotification:(AMNotification *)notification 
    notificationHandler:(AMNotificationHandler)notificationHandler 
    dialogHandler:(AMNotificationHandler)dialogHandler 
    urlHandler:(AMNotificationHandler)urlHandler 
    inAppHandler:(AMNotificationHandler)inAppHandler;
    

It’s necessary only to deliver dialogHandler for ok action to be handled. Helper methods are also available for dialog creation Only Ok action needs to be implemented.

    - (void)displayDialog:(AMNotification *)notification withOkAction:(UIAlertAction *)okAction;
    

In case of notification self-attendance, it’s necessary to run the trackNotificationCallback method to mark defined action as taken
(user saw content, clicked OK, etc.);

    - (void)trackNotificationCallback:(AMNotification *)notification;
    

Rich notification

There is possibility to add to push a picture, gif, video or audio. It is support from iOS10. To add this to Push it is needed to check flag “Rich content”. To receive it there is need to develop iOS Application. Information about setting up this type of push notifications in the application can be found here: https://developer.apple.com/documentation/usernotificationsui/customizing_the_appearance_of_notifications.
An example code snippet that can be used for handling rich notifications in an application:

        //
//  NotificationService.m
//  RichNotification
//
//  Copyright © 2016 Benhauer. All rights reserved.
//
 
#import "NotificationService.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];
 
    // Modify the notification content here...
 
    NSDictionary *userInfo = request.content.userInfo;
    if (userInfo == nil) {
        [self contentComplete];
        return;
    }
 
    NSString *payloadUrl = userInfo[@"att-url"];
    NSString *payloadType = userInfo[@"att-type"];
 
    [self loadAttachmentForUrlString:payloadUrl attachmentType:payloadType completionHandler:^(UNNotificationAttachment *attachment) {
        if (attachment) {
            self.bestAttemptContent.attachments = [NSArray arrayWithObject:attachment];
        }
        [self contentComplete];
    }];
 
 
}
 
- (void)contentComplete {
    self.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);
}
 
- (void)loadAttachmentForUrlString:(NSString *)urlString attachmentType:(NSString*) attachmentType completionHandler:(void (^)(UNNotificationAttachment *))completionHandler {
 
    __block UNNotificationAttachment *attachment = nil;
 
    
    
    NSURL *attachmentURL = [NSURL URLWithString:urlString];
 
    NSString *attType = [@"." stringByAppendingString:attachmentType];
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    [[session downloadTaskWithURL:attachmentURL
                completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
                    if (error != nil) {
                        NSLog(@"%@", error.localizedDescription);
                    } else {
                        NSFileManager *fileManager = [NSFileManager defaultManager];
                        NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:attType]];
                        [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
 
                        NSError *attachmentError = nil;
                        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];
                        if (attachmentError) {
                            NSLog(@"%@", attachmentError.localizedDescription);
                        }
                    }
                    completionHandler(attachment);
                }] resume];
}
 
 
@end
    

After implementation it is possible for example set “Attachment type” as “jpg” and “Attachment url” as a link to a picture to view this image with sended push.

Event recording

  1. Use AMMonitorViewController. SimpleId is set to be same as class name, and it might be necessary to replace it, so the opens count correctly:
        - (void)viewDidLoad {
           [super viewDidLoad];
           self.simpleId = @"secondActivity";
           // Do any additional setup after loading the view.
        }
        
  2. The direct method triggering in controlers:

     
        - (void)viewWillAppear:(BOOL)animated {
            [super viewWillAppear:animated];
            [[AMMonitor sharedInstance] trackEventWithEventType:@"START" andSimpleId:simpleId andProperties:nil];
        }
        
     
        - (void)viewWillDisappear:(BOOL)animated {
            [super viewWillDisappear:animated];
            [[AMMonitor sharedInstance] trackEventWithEventType:@"END" andSimpleId:simpleId andProperties:nil];
        }
        

  • To record clicks use the same method with eventType CLICKED parameter:
        [[AMMonitor sharedInstance] trackEventWithEventType:@"CLICKED" andSimpleId:@"click" andProperties:nil];
        
  • Recording location:
    [[AMMonitor sharedInstance] recordLocationWithLatitude:@"50.083762" andLongitude:@"19.923"];
  • Recording external event:
     
        AMProperties *properties = [[AMProperties alloc] init];
        [properties addString:@"custom event" withName:@"detail1"];
        [[AMMonitor sharedInstance] trackCustomEvent:properties withEventType:@"u_clicked"];];
        
  • Email address synchronization:
        [[AMMonitor sharedInstance] syncEmail:@"ios@test.pl"];
        
  • Phone number synchronization:
        [[AMMonitor sharedInstance] syncMsisdn:@"123123123"];
        
  • User property synchronization:
        AMProperties *properties = [[AMProperties alloc] init];
        [properties addString:@"John" withName:@"name"];
        
        
        [[AMMonitor sharedInstance] setUserProperties:properties];
        
  • Receiving unrecieved notifications:

    If application was killed from task manager it is still possible to download payload for notifications (type INAPP or NOTIFICATION) even if application wasn’t opened by tapping on notification. Reasonable place to call this method is didFinishLaunchingWithOptions method in AppDelegate.e.

        [[AMMonitor sharedInstance] loadUnrecievedNotificationsAndProcess:^(AMNotification *notification, NSError *error) {
           if (!error) {
          //Implement your logic here
               NSLog(@" %@", notification);
           }
        }];
        

Integration with beacons

For iOS available is comprehensive documentation describing integration with iBeacons https://developer.apple.com/ibeacon/ APPmanago library allows to send informations about events related to beacons with dedicated methods.

  • Entering beacon region

     
        - (void)enteredBeaconWith:(NSUUID *)uuid andMinor:(NSNumber *)minor andMajor:(NSNumber *)major;       
        
  • Exit from beacon region

     
        - (void)exitedBeaconWith:(NSUUID *)uuid andMinor:(NSNumber *)minor andMajor:(NSNumber *)major;      
        

Simple implementation may look like below:

    - (void)viewDidLoad {
       [super viewDidLoad];
       self.simpleId = @"secondActivity";
       self.locationManager = [[CLLocationManager alloc] init];
    
       if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
           [self.locationManager requestAlwaysAuthorization];
       }
    
       [self.locationManager setDelegate:self];
    
       NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:@"f7826da6-4fa2-4e98-8024-bc5b71e0893e"];
       self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:@"region"];
       [self.beaconRegion setNotifyOnEntry:YES];
       [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
       [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
    }
    
    - (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
       for (id object in beacons) {
           CLBeacon *beacon = object;
           [[AMMonitor sharedInstance] enteredBeaconWith:[beacon proximityUUID] andMinor:[beacon minor] andMajor:[beacon major]];
       }
    }
    

For more advanced use cases usage of beacon’s vendor library, like Kontakt.io may be needed. Many interesting use cases may be implemented with custom events, described earlier.

Appendix A : Push notification

Information about setting up push notifications in an app can be found here: [LINK]

Valid certificate with private key (p12 format) needs to be installed in APPmanago panel. There is plethora of tutorials online containing detailed instructions how to configure remote notifications.

The obtained certificate needs to be converted to .p12 format using Keychain Access app in macOS. It can be achieved using the “export” option available after right-clicking on the certificate.

Information about obtaining necessary certificates can be found here: [LINK]

Troubleshooting :
Application not responding for selectors from Appmanago Library:
[LINK]

If you need more information about the topic mentioned above, please contact us: support@salesmanago.com +1 800 960 0640