Simon MacDonald
Push notifications let your application notify a user of new messages or events even when the user is not actively using your application.
Analysis of the Android app store shows that 14.90% of all apps include push.
However, 42.06% of installed apps include push.
Industry wide opt in rates were at 42% in 2014.
This is down from 45% in 2013.
Source: http://bit.ly/1IsmyBr
Opt in rates tend to be higher on Android than iOS.
Provide your own UI to explain how push notifications will be used in your app. Then have the system prompt for permission
This can boost opt in rates up to 60%
Don't rely on the operating system to prompt your users.
Guess what is the top reason why people uninstall apps?
Yup, annoying push notifications!
Don't send too many push notifications. Social apps can get away with sending more push notifications than a promotional app can.
Make it as easy as possible for your users to configure which types of push notifications they want to receive.
Good
Better
Because push messages are generally paired with sound or phone vibrations, timing should never be overlooked. You don’t want to send a push notification to your app users at 3am and unleash the wrath of customers whose sleep has been disturbed or worse yet, the wrath of their significant other.
Don't make the assumption your users will schedule Do not disturb on iOS or setup Android's Priority Mode.
App
Push Service
App Server
register()
registration ID
save registration ID
Android
pushNotification.register(
successHandler,
errorHandler, {
"senderID":"sender_id",
"ecb":"onNotificationGCM"
});
iOS
if ( device.platform == 'android' ){
} else if ( device.platform == 'ios' ) {
pushNotification.register(
tokenHandler,
errorHandler, {
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"onNotificationAPN"
});
}
Windows
else if (device.platform == 'windows') {
pushNotification.register(
channelHandler,
errorHandler, {
"channelName":"channel_id",
"ecb":"onNotificationWindows"
});
}
Android
function onNotificationGCM(e) {
switch( e.event ) {
case 'registered':
console.log("regID = " + e.regid);
break;
}
}
iOS
Windows
function tokenHandler (result) { console.log('regID = ' + result); }
function channelHandler(result) {
console.log('regID = ' + result.uri);
}
Android: result.regid
iOS: result
Windows: result.uri
a code smell is a surface indication that usually corresponds to a deeper problem in the system
Martin Fowler, 1999
Android
var push = PushNotification.init({
"android": {
"senderId": "sender_id"
},
"ios": {
"badge":"true",
"sound":"true",
"alert":"true"
},
"wp": {
"channelName": "channel_id"
}
});
push.on('registration', function(data) {
console.log('regID = '
+ data.registrationId);
});
iOS
Windows
Let's talk about...
App
Push Service
App Server
send push
send push
Android
iOS
Windows
function onNotificationGCM(e) {
switch( e.event ) {
case 'message':
navigator.notification.alert(
e.message,null,e.title);
break;
case 'registered':
console.log("regID = " + e.regid);
break;
}
}
function onNotificationAPN(e) {
navigator.notification.alert(
e.alert,null,e.title);
}
function onNotificationWindows(e) {
navigator.notification.alert(
e.Message,null,e.Title);
}
Android
push.on('notification', function(data) { navigator.notification.alert( data.message,null,data.title);
});
iOS
Windows
Same data structure across all platforms.
interface Notification {
String title;
String message;
String sound;
Integer count;
JsonObject additionalData;
};
Pull is when an application requests, downloads and utilizes new data.
App
App Server
XHR Request
XHR Response
Download Request 1
Download Response 1
...
Download Request n
Download Response n
Make a XHR Request
var xhr = new XMLHttpRequest();
xhr.onload = function() {
Parse the response
Make additional network requests for more content
for (var i=0; i < books.length; i++) {
var ft = new FileTransfer();
ft.download(
books[i].cover_image,
fileURL,
function(entry) {
console.log("download complete");
},
function(error) {
console.log(error.source);
},
false, {});
}
var books =
JSON.parse(this.responseText);
};
xhr.open("get", "books.json", true);
xhr.send();
Network Requests | Total File Size |
---|---|
11 | 336 kb |
but for one request for new content we've made:
we can do a lot better if we zip up all the content into a single file.
App
App Server
Download Request 1
Download Response 1
Download your zipped content
Unzip the response
var ft = new FileTransfer();
ft.download(
'https://myserver.com/books.zip',
fileURL,
function(entry) {
console.log("download complete");
},
function(error) {
console.log(error.source);
},
false, {});
zip.unzip(
entry.toUrl(),
destinationDir,
function(result) {
console.log("unzip complete");
},
function(progressEvent) {
console.log(progressEvent.loaded
/ progressEvent.total);
}
);
Network Requests | Total File Size |
---|---|
11 1 |
336 kb 241 kb |
but why am I still not happy?
It's overly complicated to use and both the FileTransfer and Unzip plugins require you to use it in order for you to specify destinations.
As well, the Unzip plugin doesn't have support for Windows Phone.
Callback Hell anyone?
var fileURL; window.requestFileSystem( LocalFileSystem.PERSISTENT, 0, function(fs) { fs.root.getFile("tempFile.zip", {create: true, exclusive: false}, function(entry) { fileURL = entry.toURL(); }, function(error) { console.log(error.code); }); },
function(error) { console.log(error.code); });
Just call sync
var sync = ContentSync.sync({
src: 'http://myserver.com/books.zip',
id: 'books'
});
sync.on('progress', function(data) {
console.log(data.progress);
});
sync.on('complete', function(data) {
console.log('Sync complete');
console.log(data.localPath);
});
var sync = ContentSync.sync({
src: 'http://myserver.com/books.zip',
id: 'books',
type: 'replace'
});
sync.on('complete', function(data) {
console.log('Sync complete');
console.log(data.localPath);
});
var sync = ContentSync.sync({
src: 'http://myserver.com/books.zip',
id: 'books',
type: 'local'
});
sync.on('complete', function(data) {
console.log('Sync complete');
console.log(data.localPath);
});
var sync = ContentSync.sync({
src: 'http://myserver.com/books.zip',
id: 'books',
type: 'merge'
});
sync.on('complete', function(data) {
console.log('Sync complete');
console.log(data.localPath);
});
var sync = ContentSync.sync({
src: 'http://myserver.com/books.zip',
id: 'books',
type: 'merge',
copyCordovaAssets: true
});
sync.on('complete', function(data) {
console.log('Sync complete');
console.log(data.localPath);
});
3.3.2 An Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exception to the foregoing is scripts and code downloaded and run by Apple's built-in WebKit framework, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.