Petr Dvořák: Push notifikace ve velkém

Post on 16-Feb-2017

205 views 0 download

transcript

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