Download - Petr Dvořák: Push notifikace ve velkém
PETR DVOŘÁKCEO at Lime
Push notifications. The big way.
Should be part of every app.
Are not part of every app.
Are simple when done simple.
Mostly, they are pretty hard.
Push notifications
Why?
Users who opt in to push messages averaged 3x more app launches than those who opted out.
— Localytics
65% of users returned to an app in the 30 days after the app’s initial download, if they have push enabled.
— Localytics
Forced social login
Privacy concerns
Intrusive ads
Bad UI/UX
Freezing
Complex sign-up
Annoying notifications
0 20 40 60 80
71%
68%
48%
42%
29%
23%
19%
Top 7 reasons why people uninstall mobile apps
Source: Appiterate Survey, as % of all respondent (each respondent picked 3 reasons)
Personalized and relevant content.
Delivered in the right time.
Spamming users results in uninstalls.
Test, measure, improve.
Automate engagement.
When it works?
Utility & Finance
Taxi & Ride sharing
Travel & Hospitality
Sport
Food & Beverage
Media
Social
Retail & e-commerce
0 10 20 30 40
11%
13%
19%
25%
26%
29%
30%
40%
Push notification engagement by industry
Source: Kahuna
In 2015, 49.8% of app users opted in to receiving push notifications. In 2014, it was 52.0%.
— Localytics
Implementation topics
APNs / GCM
Provider
1. Register device at APNs and provider.
2. Identify (or segment) users.
3. Send push notifications.
4. Analyze data.
5. Remove unused devices.
What needs to be done?
Device registration
Registration
APNs
Provider
1. Ask for APNs token.
Registration
APNs
Provider
1. Ask for APNs token.
2. Get APNs token
Registration
APNs
Provider
1. Ask for APNs token.
2. Get APNs token
3. Send APNs token to provider
APNs token
User identifier
Push notification settings
User demographics
User location
… and much more :-)
Data sent to provider
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *set = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:set]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }
AppDelegate.m
Register for remote notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *set = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:set]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }
AppDelegate.m
Register for remote notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *set = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:set]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }
AppDelegate.m
Register for remote notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *set = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:set]; [[UIApplication sharedApplication] registerForRemoteNotifications]; }
AppDelegate.m
Register for remote notifications
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken { self.registered = YES; [self sendProviderDeviceToken:devToken]; // custom method } - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { NSLog(@"Error in registration. Error: %@", err); }
AppDelegate.m
Register for remote notifications
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken { self.registered = YES; [self sendProviderDeviceToken:devToken]; // custom method } - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { NSLog(@"Error in registration. Error: %@", err); }
AppDelegate.m
Register for remote notifications
Sending notifications
Send push notifications
APNs
Provider
1. Decide to send push
Send push notifications
APNs
Provider
1. Decide to send push
2. Send payload and APNs token
Send push notifications
APNs
Provider
1. Decide to send push
2. Send payload and APNs token
3. Deliver the push message
Push notification payload
{ "aps" : { "alert" : { "title" : "Hello there!", "body" : "It has been a while." } } }
Maximum payload length is 4096 bytes.
Ehm… security?
Ehm… security?Push Certifikát (*.pem, *.p12)
$ openssl pkcs12 -in apns-dev-cert.p12 -out apns-dev-cert.pem -nodes -clcerts
Ehm… security?Push Certifikát (*.pem, *.p12)
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$devices = new DeviceCollection(array( new Device('???????') ));
$message = new Message('Hello guys!');
$push = new Push($apnsAdapter, $devices, $message); $pushManager->add($push); $pushManager->push();
Send push notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; NSDictionary *userInfo = localNotif.userInfo;
}
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
Handle push notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; NSDictionary *userInfo = localNotif.userInfo;
}
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
Handle push notifications
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; NSDictionary *userInfo = localNotif.userInfo;
}
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
}
Handle push notifications
Feedback service
Feedback service
APNs
Provider
1. Request expired tokens
Feedback service
APNs
Provider
1. Request expired tokens
2. Expired token list
Feedback service
APNs
Provider
1. Request expired tokens
2. Expired token list
3. Delete tokens
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$feedback = $pushManager->getFeedback($apnsAdapter); // $feedback contains collection of [device_token, timestamp] pairs
Clean up device tokens
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$feedback = $pushManager->getFeedback($apnsAdapter); // $feedback contains collection of [device_token, timestamp] pairs
Clean up device tokens
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$feedback = $pushManager->getFeedback($apnsAdapter); // $feedback contains collection of [device_token, timestamp] pairs
Clean up device tokens
$pushManager = new PushManager(PushManager::ENVIRONMENT_DEV);
$apnsAdapter = new ApnsAdapter(array( 'certificate' => '/path/to/your/apns-certificate.pem', ));
$feedback = $pushManager->getFeedback($apnsAdapter); // $feedback contains collection of [device_token, timestamp] pairs
Clean up device tokens
Silent push notifcation
Interactive actions
Localization
Badge number
Custom sound
Extras
Scaling big
User management - fast segmentation… by what?
Delivery time - 100k devices x 4096 … 1M devices?
Multiple apps - Reusing the push server
Hardware and infrastructure
Engineering - project grows big, multiple platforms
Dimensions of scaling
APNS / GCM
Provider
APNs
Provider (business logic)
GCM
SDK SDKApp Management Service Device Registration Service User Segmentation Service
Analytics Data Service
APNS Node GCM Node
APNs
Provider (business logic)
App Management Service Device Registration Service User Segmentation Service
Analytics Data Service
GCM
APNS Node GCM Node
SDK SDK
APNs
Provider (business logic)
GCM
SDK SDK
APP KEY
App Management Service Device Registration Service User Segmentation Service
Analytics Data Service
APNS Node GCM Node
APP KEYAPP KEY
1. Create campaign object
2. Select the audience
3. Send notifications
4. Collect analytics
Campaign planning
APNs
Provider (business logic)
GCM
SDK SDKApp Management Service Device Registration Service User Segmentation Service
Analytics Data Service
APNS Node GCM Node
APNs
Provider (business logic)
GCM
SDK SDKApp Management Service Device Registration Service User Segmentation Service
Analytics Data Service
APNS Node GCM Node
APNs
Provider (business logic)
GCM
SDK SDKApp Management Service Device Registration Service User Segmentation Service
Analytics Data Service
MongoDBMongoDBMongoDB
APNS Node GCM Node
N instances
Partitioning
APNs GCM
SDK SDKAPI, admin, workers
IBM Bluemix - price, HW scaling, connectivity, …
MongoDB - performance, queries, … (Compose)
Node.js - for “nodes”, speed, efficiency, …
RESTful API - standards, easy to work with, … (PHP, Nette)
Server Technologies
Device registration
Push notification processing
UI Features
User profile synchronization
Location monitoring
Analytics
Mobile SDK
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { LimeEngate *lime = [LimeEngage sharedInstance];
[lime startFetchingContextForApplicationKey:@“app_8cb31c49a46df1fea11"];
[lime handleRemoteNotificationInfo:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]]; return YES; }
Set up SDK
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { LimeEngate *lime = [LimeEngage sharedInstance];
[lime startFetchingContextForApplicationKey:@"app_8cb31c49a46df1fea11"];
[lime handleRemoteNotificationInfo:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]]; return YES; }
Set up SDK
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { LimeEngate *lime = [LimeEngage sharedInstance];
[lime startFetchingContextForApplicationKey:@"app_8cb31c49a46df1fea11"];
[lime handleRemoteNotificationInfo:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]]; return YES; }
Handle notifications
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
LimeEngage *lime = [LimeEngage sharedInstance]; [lime handleRemoteNotificationInfo:userInfo];
}
Handle notifications
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
LimeEngage *lime = [LimeEngage sharedInstance]; [lime handleRemoteNotificationInfo:userInfo];
}
Handle notifications
LimeEngage *lime = [LimeEngage sharedInstance];
lime.currentUser.internalId = @"4378924"; lime.currentUser.name = @"Jan"; lime.currentUser.surname = @"Novak"; lime.currentUser.sex = @"male";
[lime synchronizeCurrentUser];
Update user info
LimeEngage *lime = [LimeEngage sharedInstance];
lime.currentUser.internalId = @"4378924"; lime.currentUser.name = @"Jan"; lime.currentUser.surname = @"Novak"; lime.currentUser.sex = @"male";
[lime synchronizeCurrentUser];
Update user info
Demo
Open-source04/2016
Thank you! :)
@joshis_tweets http://getlime.io/
WWW.MDEVTALK.CZ
mdevtalk