Maxim Salnikov

@webmaxru

Native Web Apps:

Are We There Yet?

What makes an app feels

like native

and what about web apps

Maxim Salnikov

  • PWA Summit co-organizer

  • PWA Oslo / PWA London meetups organizer

  • Google Dev Expert in Web Tech / Capabilities & Installability

Developer Audience Lead at Microsoft

Web as an app platform

  • Historically depends on the "connection status"

  • Works within the browser context, not underlying platform

  • Evergreen browsers

  • Versatile language

  • Performant JS engines

  • Excellent tooling

  • Huge community

🎂

Web Almanac 2022

Installable websites

~1,500,000 websites may be installable on mobile home screens, offering an app experience.

Reach

"Nativeness"

Native

Web

Web applications can reach anyone, anywhere, on any device with a single codebase.

Nativeness = Number & depth of integrations with the platform

Native VS Web

What's available?

  • Installation & run

  • Windowing

  • Outbound integrations

  • Inbound integrations

  • Notifications

  • Background tasks

  • Access to hardware

Demo app

edge://flags

Installation & run

  • Installation

  • Uninstallation

  • Run on user login

  • Shortcuts

  • Launch mode

Installation & run

  • Installation

  • Uninstallation

  • Run on user login

  • Shortcuts

  • Launch mode

Web App Manifest

Service Worker with "fetch"

{

{
  "name": "BPM Techno",
  "short_name": "BPM Techno Counter",
  "start_url": "?utm_source=homescreen",
  "display": "standalone",
  "background_color": "#fff",
  "description": "A free online BPM counter",
  "icons": [{
    "src": "images/touch/48x48.png",
    "sizes": "48x48",
    "type": "image/png"
  }]
}

site.webmanifest

Installation & run

  • Installation

  • Uninstallation

  • Run on user login

  • Shortcuts

  • Launch mode

"shortcuts": [
  {
    "name": "Upload MP3 File",
    "short_name": "Upload MP3r",
    "description": "Count BPM of the uploaded file",
    "url": "/upload-mp3?utm_source=homescreen",
    "icons": [{ "src": "/icon-mp3.png", "sizes": "192x192" }]
  }
]

site.webmanifest

Installation & run

  • Installation

  • Uninstallation

  • Run on user login

  • Shortcuts

  • Launch mode

"launch_handler": {
  "route_to": "new-client" | "existing-client" |
    "auto",
  "navigate_existing_client": "always" | "never",
}

site.webmanifest

launchQueue.setConsumer(launchParams => {
  const url = launchParams.targetURL;
});

main.js

Windowing

  • Main window mode

  • Title bar options

  • Status bar options

  • Tabbed experience

Windowing

  • Main window mode

  • Title bar options

  • Status bar options

  • Tabbed experience

{
  "display": "fullscreen" | "standalone" |
    "minimal-ui" | "browser",
  "display_override": ["window-control-overlay",
    "minimal-ui"],
}

site.webmanifest

Windowing

  • Main window mode

  • Title bar options

  • Status bar options

  • Tabbed experience

titlebar-area-x
titlebar-area-y
titlebar-area-width
titlebar-area-height

CSS Variables

navigator.windowControlsOverlay.
  getBoundingClientRect()

navigator.windowControlsOverlay.visible

JavaScript API

Windowing

  • Main window mode

  • Title bar options

  • Status bar options

  • Tabbed experience

{
  "display_override": "tabbed"
}

site.webmanifest

Outbound integrations

  • Web Share API

  • Contact Picker API

  • File Access API

Outbound integrations

  • Web Share API

  • Contact Picker API

  • File Access API

if (navigator.share) {
  navigator.share({
    title: 'BPM Techno',
    text: 'Check out BPMTech.no',
    url: 'https://bpmtech.no',
  })
    .then(() => console.log('Successful share'))
    .catch((err) => console.error(err));
}

main.js

Outbound integrations

  • Web Share API

  • Contact Picker API

  • File Access API

const props = ['name', 'email', 'tel'];
const opts = {multiple: true};

try {
  const contacts =
    await navigator.contacts.select(props, opts);
  handleResults(contacts);
} catch (err) {
  console.error(err));
}

main.js

Outbound integrations

  • Web Share API

  • Contact Picker API

  • File Access API

let openFileHandle;
btnOpen.addEventListener('click', async () => {
  [openFileHandle] =
    await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
});

main.js

const saveFileHandle =
  await window.showSaveFilePicker();
const directoryHandle = 
  await window.showDirectoryPicker();

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

"url_handlers": [
  {
    "origin": "https://bpmtech.no"
  },
  {
    "origin": "https://partnerapp.com"
  }
]

site.webmanifest

"web_apps": [
  {
    "manifest": "https://partnerapp.com/manifest.json",
    "details": {
      "paths": [
        "/public/data/*"
      ]
    }
  }
]

.well-known/web-app-origin-association

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

"capture_links":
  "none" | "new-client" |
    "existing-client-navigate"

site.webmanifest

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

"protocol_handlers": [
  {
    "protocol": "web+bpm",
    "url": "index.html?bpm=%s"
  }
]

site.webmanifest

web+bpm://bpm=120

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

"file_handlers": [
  {
    "action": "/open-mp3",
    "accept": { "text/csv": [".mp3"] },
    "icons": [
      {
        "src": "./images/mp3-file.png",
        "sizes": "144x144"
      }
    ]
  }
]

site.webmanifest

window.launchQueue.setConsumer(launchParams => {
  const fileHandle = launchParams.files[0];
});

main.js

Inbound integrations

  • URL handlers

  • Declarative Link Capturing

  • Protocol handlers

  • File type handlers

  • Share target

"share_target": {
  "action": "/share",
  "method": "GET",
  "params": {
    "title": "title",
    "text": "text",
    "url": "url"
  }
}

site.webmanifest

window.addEventListener('DOMContentLoaded', ()=> {
  const parsedUrl = new URL(window.location);
  const url = parsedUrl.searchParams.get('url');
});

main.js

Notifications

  • Web Push API

  • Badging

Notifications

  • Web Push API

  • Badging

navigator.setAppBadge(42).catch((err) => {
  console.error(err)
});

navigator.clearAppBadge().catch((err) => {
  console.error(err)
});

main.js

Background tasks

  • Background Sync

  • Periodic BG Sync

  • Background Fetch

  • Payment Handler

Access to hardware

  • Audio & Video Capture
  • Geolocation
  • Web Bluetooth
  • Web MIDI API
  • Magnetometer API
  • Web NFC API
  • Device Memory API
  • Network Information API
  • Battery Status API
  • Ambient Light Sensor
  • Proximity Sensor
  • WebHID
  • Serial API
  • Web USB
  • User Idle Detection

Where to follow?

🐡

How to experiment at scale?

Native Web: Are We There?

  • Installation & run

  • Windowing

  • Outbound integrations

  • Inbound integrations

  • Notifications

  • Background tasks

  • Access to hardware

  • Source code

  • Extra features

  • Demo hosted on Azure Static Web Apps

Extended PWA example

Thank you!

@webmaxru

Maxim Salnikov

Questions?

@webmaxru

Maxim Salnikov

Where to host your native web app?

Native Web Apps: Are We There Yet?

By Maxim Salnikov

Native Web Apps: Are We There Yet?

There are so many discussions about web VS native apps. Will we get to the point where the Web becomes truly Native for the majority of the platforms? Progressive Web Apps, Project Fugu, WebAssembly & other technologies actively contribute to moving in this direction. In this session, we go through a list of details that make the integration of an application & operating system seamless, and map it to the APIs available for the web platform. With the knowledge of what's available today and what's coming soon, you are empowered to build truly Native Web Apps to deliver the best user experience!

  • 3,512