This explainer is for the Shortcut API
Some platforms like Windows and Android have ways to add menu items to the app launcher icon itself, here after called shortcuts. These can perform certain app actions. On Android in addition, you can drag these shortcuts to the homescreen.
The shortcut list (aka menu) contains a mixture of static and dynamic shortcuts and has a limit which may be different per platform or per launcher used. The amount of shortcuts on the homescreen is not limited so though the shortcut list may change overtime, existing shortcuts on the homescreen can still be updated (say change icon) though not removed.
The shortcuts are quite useful as /shortcuts/ to common actions, like starting a chat with the person you chat with the most, or say launch the web app to a specific place inside the app (deep link).
These are becoming quite popular, like for instance in GMaps you can add a shortcut to your contributions page.
The ShortcutInfo dictionary is reusing name, short_name and icons from the manifest specification and can itself be embedded in the manifest.json inside the shortcuts
member which takes an array of ShortcutInfo
.
dictionary ShortcutInfo {
DOMString id;
DOMString name;
DOMString short_name;
sequence icons;
dictionary data;
}
partial interface Navigator {
readonly attribute int maxVisibleShortcuts;
sequence<ShortcutInfo> getManifestShortcuts();
sequence<ShortcutInfo> getDynamicShortcuts();
Promise addShortcut(ShortcutInfo);
Promise removeShortcut(ShortcutInfo);
Promise setShortcut(ShortcutInfo);
Promise requestPinShortcut(ShortcutInfo);
};
[Constructor(DOMString type, optional ShortcutEventInit eventInitDict), Exposed=ServiceWorker]
Interface ShortcutEvent : ExtendableEvent {
readonly attribute DOMString id;
readonly attribute any data;
};
{
"name": "Cool Email",
"start_url": "https://coolemail.com",
…
"shortcuts": [{
"name": "Email Mom",
"id": "send-email",
"data": { "email": "[email protected]" },
"icons": [{
"src": "icon/hd_hi.svg",
"sizes": "any"
}]
}]
}
Similarly to the Push API, when shortcuts are clicked, the shortcutclick
event is fired in the Service Worker.
At that point you have access to the id and data of the shortcut and can easily access any existing open window for the PWA or open the app using openWindow
. postMessage
can be used to talk to the open instance.
This is quite flexible. Using a Service Worker, the app can load URLs, execute JavaScript and communicate with a running instance of the PWA, even without giving it focus.
self.addEventListener('shortcutclick', function(event) {
console.log('[Service Worker] Shortcut click Received.');
if (event.id != "send-email) {
return;
}
async function getWindow() {
const client = await clients.matchAll(options).then(function(clientList) {
if (clients.length > 0) {
return clients[0];
}
return clients.openWindow('https://inbox.coolemail.com');
});
client.postMessage({ action: event.id, data: event.data });
await client.focus();
}
event.waitUntil(getWindow());
});
navigator.requestPinShortcut({
name: "Email Mom",
id: "send-email",
data: { email: "[email protected]" },
icons: [{
src: "icon/hd_hi.svg",
sizes: "any"
}]
}).then(info => {
console.log(`Added ${info.title} shortcut`);
})
As it is quite a common case to just load different URLs (deep links into the app), it makes sense to add start_url
to the ShortcutInfo
. Questions is how to handle that. Without Service Worker it could just load the URL direclty.
- Should
shortcutclick
be dispatched when astart_url
is available as the URL might already be part offetch
event if in scope?
https://developer.android.com/guide/topics/ui/shortcuts.html https://developer.android.com/preview/features/pinning-shortcuts-widgets.html https://docs.microsoft.com/en-us/uwp/api/windows.ui.startscreen.jumplist https://github.com/android/platform_frameworks_base/blob/master/core/java/android/content/pm/ShortcutManager.java https://developer.android.com/reference/android/content/pm/ShortcutManager.html https://developer.android.com/reference/android/content/pm/ShortcutInfo.html https://developer.apple.com/ios/3d-touch/ https://developer.apple.com/documentation/uikit/uiapplicationshortcutitem https://electron.atom.io/docs/tutorial/desktop-environment-integration/
I have some initial thoughts:
A bigger deal:
I'm concerned that these "deep link shortcuts" are not URLs. The shortcut contains an opaque JavaScript object that will be sent into the SW event upon opening the shortcut, which feels very un-webby given that the web is glued together with URLs. Essentially these shortcuts are deep links into the app, but rather than being URLs (the web's preferred method of deep linking), they are opaque objects.
Why can't the shortcut just be a URL within the app's manifest scope? That means the "P" in PWA is working correctly: you design your app with a sensible URL scheme, and links from the web can deep link into your app. As a progressive enhancement, you can also create OS shortcuts to those same deep places in your app.
This would limit the capabilities (it would have to open a new page, rather than be magically handled by a service worker), but I think that's a good starting point. If we want links into the app to be grabbed by the SW and not open a new browser tab (something we're thinking about now in a bunch of places: a) for regular web links into an installed app, and b) for Web Share Target), we should solve that generally, maybe adding a SW event similar to fetch, but at a UI level (not a network level), that lets you intercept a navigation, stop it from opening a new tab, and do something in the background. WDYT?