(I make websites.)
I make websites.
I want you to build me a website!
The Client
Sounds good.
me
I want you to build me a mobile app!
The Client
Okay, but based on what you're trying to do, a mobile app might not be the best solution. What about a website instead?
me
I want you to build me a mobile app, and I'll pay you a lot of money to do it.
The Client
Okay, let's build a mobile app.
me
Building an app in Cordova...
Actually, a few problems...
So, I built a mobile app...
...and then I made a small change.
From: Google Play Support
To: meWe have rejected your latest build from the Google Play Store, because it violates our policy on embedded YouTube videos.
Please see this webpage that has a whole lot of text, which kind of sort of describes what I'm talking about.
From: me
To: Google Play SupportOkay, thank you so much for your note. I am confused on the cause though, as my app does not have any embedded YouTube videos.
Can you please explain?
From: Google Play Support
To: meThe embedded YouTube videos in your app violate our policy on embedded YouTube videos.
From: me
To: Google Play SupportRight... Except the app doesn't have any YouTube videos, of any sort.
Please advise.
From: Google Play Support
To: me
We do not offer support.
From: Google Play Support
To: me
We do not offer support.
This is a specific problem.
pointing to a greater concern.
So, when you say you want a mobile app, what do you really want?
what I should have said
“I want something that...
A spec written by...
Supported by
What is a PWA?
Potentially a lot of things.
What can be in a PWA?
What do you need?
manifest.json
service-worker.js
Where can I get a HTTPS certificate?
PS: For free?
Regardless: get a cert, turn it on, and direct all HTTP traffic to HTTPS.
manifest.json
A json file located in the root of your project, which will define some values...
service-worker.js
A single javascript file (can be called anything), which will set up functions for installation, caching, permissions, and features.
Reference both these files in your index.html
The service worker gets its own lifecycle.
I need to be able to party efficiently when I don't have internet.
a friend
That's an excellent point!
me
service-worker.js
const offlineUrl = '/index.html';
const offlineFiles = [
'/index.html',
'/manifest.json',
'/css/party.css',
'/js/min/jquery.min.js',
'/js/min/howler.min.js',
'/js/min/party.min.js',
'/audio/party.mp3',
'/audio/venga.mp3'
];
/*****
On install, the service worker will take all our
required files, and store those locally. */
self.addEventListener('install', function(event) {
var offlineRequest = new Request(offlineFiles);
event.waitUntil(
fetch(offlineRequest).then(function(response) {
return caches.open('offline').then(function(cache) {
return cache.put(offlineRequest, response);
});
})
);
});
/************
Then, on a fetch request, the service worker
will check for response.
If everything seems to be offline, serve the
offline files instead. */
self.addEventListener('fetch', function(event) {
var request = event.request;
if (request.method === 'GET') {
event.respondWith(
fetch(request).catch(function(error) {
console.error(
'[onfetch] Failed. Serving cached offline fallback ' +
error
);
return caches.open('offline').then(function(cache) {
return cache.match(offlineUrl);
});
})
);
}
});
manifest.json
{
"manifest_version": 2,
"name": "PARTY PARTY PARTY PARTY PARTY",
"short_name": "PARTY",
"description": "PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY PARTY",
"default_locale": "en-us",
"display": "standalone",
"scope": "/",
"start_url": "/",
"theme_color": "#d1d1d1",
"background_color": "#d1d1d1",
"icons": [
{
"src": "/icon-36x36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/icon-48x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
index.html
// Register the service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
// Registration was successful
}).catch(function(err) {
// registration failed :(
});
}
service-worker.js
const cacheName = 'v2.17';
const offlineUrl = '/offline.html';
const offlineFiles = [
'/index.html',
'/offline.html',
'/manifest.json',
'/css/damn.css',
'/js/libraries/vue.min.js',
'/js/min/damn.min.js',
'/svg/offline-dog.svg'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(cacheName);
})
);
});
self.addEventListener('fetch', function(event) {
// Only fall back for HTML documents.
var request = event.request;
// && request.headers.get('accept').includes('text/html')
if (request.method === 'GET') {
// `fetch()` will use the cache when possible, to this examples
// depends on cache-busting URL parameter to avoid the cache.
event.respondWith(
fetch(request).catch(function(error) {
// `fetch()` throws an exception when the server is unreachable but not
// for valid HTTP responses, even `4xx` or `5xx` range.
console.error(
'[onfetch] Failed. Serving cached offline fallback ' +
error
);
return caches.open('offline').then(function(cache) {
return cache.match(offlineUrl);
});
})
);
}
// Any other handlers come here. Without calls to `event.respondWith()` the
// request will be handled without the ServiceWorker.
});
The Notification API
service-worker.js
// After the install code...
Notification.requestPermission(result => {
if (result === 'granted') {
//permission granted.
navigator.serviceWorker.ready.then(registration => {
registration.showNotification(
'Damn Dog has been updated!', {
icon: '/android-chrome-96x96.png',
//vibrate: [500, 100, 500],
body: '7 new rounds have been added to damn.dog,
bringing the total up to 358.',
tag: 'game-updated'
});
});
}
});
Q: How do I push to users not currently on the website?
A: The same way you'd handle any other kind of push subscription
When thinking of
Make things better. It's your responsibility.