Tag: Push

Why would a business push a native app over a website?

I wanted to write down what I think the reasons are here in December of 2021 so that we might revisit it from time to time in the future and see if these reasons are still relevant. I’m a web guy myself, so I’m interested in seeing how the web can evolve to mitigate these concerns.

I’m exclusively focusing on reasons a native app might either be a distinct advantage or at least feel like an advantage compared to a website. Nothing subjective here, like “it’s faster to develop on” or “native apps are more intuitive” or the like, which are too subjective to quantify. I’m also not getting into reasons where the web has the advantage. But in case you are unsure, there are many: it’s an open standardized platform, it will outlast closed systems, it strongly values backward compatibility, anybody can build for the web, it runs cross-platform, and heck, URLs alone are reason enough to go web-first. But that’s not to say native apps don’t have some extremely compelling things they offer, hence this post.

Because they get an icon on the home screen of the device.

It’s a mindshare thing. You pick up your phone, the icon is staring you in the face, begging to be opened. A widely cited report from a few years back suggests 90% of phone usage is in apps (as opposed to a mobile web browser), even though there is plenty of acknowledged gray area there. So, theoretically, you get to be part of that 90% if you make an app rather than being booted into the sad 10% zone.

If reach is the top concern, it seems like the best play is having both a web app and a native app. That way, you’re benefitting from the share-ability and search-ability that URLs give you, along with a strong presence on the device.

Looking at my own phone’s home screen, the vast majority of the apps are both native and web apps: Google Calendar, AccuWeather, Google Maps, Spotify, Notion, Front, Pocket Casts, Instagram, Discord, Twitter, GitHub, Slack, and Gmail. That’s a lot of big players doing it both ways.

Potential Solution: Both iOS and Android have “Add to Home Screen” options for websites. It’s just a fairly “buried” feature and it’s unlikely all that many people use it. Chrome on Android goes a step further, offering a Native App Install Prompt for apps for Progressive Web App (PWA) websites that meet a handful of extra criteria, like the site has been interacted with for at least 30 seconds. Native App Install Prompts are a big tool that levels this playing field, and it would be ideal to see Apple support PWAs better and offer this. There isn’t that much we can do as website authors; we wait and hope mobile operating systems make it better. There is also the whole world of software tools where what you build can be delivered both as a web app and native app, like Flutter.

Because they launch fast.

Native apps have a bunch of the resources required to run the app locally meaning they don’t need to get them from the network when opened.

Potential Solution: This is only partially true, to begin with. When you download the app, you’re downloading the resources just like the web does. The web caches resources by default, and has more advanced caching available through Service Workers. PWAs are competitive here. Native apps aren’t automatically faster.

Because it’s harder for users to block ads and easier to collect data.

There are all sorts of ad blockers for mobile.

But those work only in mobile web browsers, not native apps. If you’d like to block ads in native apps… too bad, I guess? If the point of the thing you are building is to show ads or track users, well, I guess you’ll do better with a native app, assuming you can get the same kind of traffic to it as you can a website (which is questionable).

I’m hesitant to put “because native apps are more secure” as a reason on this list because of the fact that, as a user, you have so little control over how resources load that it doesn’t feel like an increased security risk to me. But the fact that native apps typically go through an approval process before they are available in an app store does offer some degree of protection that the web doesn’t have.

Potential Solution: Allow ad/tracker blocking apps to work with native apps.

Because users stay logged in.

This is a big one! Once you’re logged in to a native app, you tend to stay logged in until you specifically log out, or a special event happens like you change your password on another device. Web apps seem to lose login status far more often than one might like, and that adds to a subconscious feeling about the app. When I open my native Twitter app, it just opens and it’s ready to use. If I thought there was a 30% chance I’d have to log in, I’m sure I’d use it far less. (And hey, that might be a good thing, but a business sure won’t think so.)

There is also a somewhat awkward thing with web apps in that, even if you’re logged in on your mobile devices primary browser, you won’t necessarily be logged into some other app’s in-app browser context — whereas, with native apps, they often intercept links and always open in the native app context.

Potential Solution: There isn’t any amazing solution here. It’s largely just trickery. For example, long cookie expiration dates (six months is about what you can get if you’re lucky, I hear). Or you can do a thing where you keep a JSON Web Token (JWT) in storage and do a rolling re-auth on it behind the scenes. There are some other solutions in this thread, many of which are a bit above my head. Making the log in experience easier is also a thing, like using oAuth or magic email links, but it’s just not the same as that “always logged in” feeling. Maybe smart browser people can think of something.

Because the apps can have that “native feel.”

Typically meaning: fast and smooth, but also that they look the way other apps on that platform look and feel. For example, Apple offers SwiftUI which is specifically for building native apps using pre-built componentry that looks Apple-y. Replicating all that on the web is going to be hard. You have to work your ass off to make it as good as what you get “for free” with something like SwiftUI.

Potential Solution: Mobile platform creators could offer UI kits that bring the design language of that mobile platform to the web. That’s largely what Google has done with Material, although the web version of it isn’t ready to use yet and is just considered a “planned” release.

Because they aren’t sharing a virtual space with competitors a tap away.

There is a sentiment that a web browser is just the wild west as you aren’t in control of where your users can run off to. If they are in your native app, good, that’s where you want them. If they are in a web browser, you’re sharing turf with untold other apps rather than keeping them on your holy ground.

Potential Solution: Get over it. Trying to hide the fact that competitors exist isn’t a good business strategy. The strength of the web is being able to hop around on a shared, standardized, open platform.

Because they get the full feature set of APIs.

All the web can hope for is the same API access as native apps have. For example, access to a camera, photos, GPS, push notifications, contacts, etc. Native apps get the APIs first, then if we’re lucky, years later, we get some kind of access on the web.

Developers might literally be forced into a native app if a crucial API is missing on the web.

One big example is push notifications. Are they generally annoying? Yes, but it’s a heavily used API and often a business requirement. And it makes plenty of sense for lots of apps (“Your turn is coming up in 500 feet,” “Your driver is here,” “Your baggage will be arriving on carousel 9,” etc.). If it’s crucial your app has good push notifications, the web isn’t an option for your app on iOS.

Potential Solution: The web should have feature parity with device APIs and new APIs should launch for both simultaneously.

Because there is an app store.

This is about discoverability. Being the one-and-only app store on a platform means you potentially get eyeballs on your app for free. If you make the best Skee-Ball game for a platform, there is a decent chance that you get good reviews and end up a top search for “Skee-Ball” in that app store, gaining you the majority share of whatever that niche market is. That’s an appealing prospect for a developer. The sea is much larger on the web, not to mention that SEO is a harder game to play and both advertising and marketing are expensive. A developer might pick a native app just because you can be a bigger fish right out of the gate than you can on the web.

And yet, if you build an app for listening to music, you’ll never beat out the major players, especially when the makers of the platform have their own apps to compete with. The web just might offer better opportunities for apps in highly competitive markets because of the wider potential audience.

Potential Solution: Allow web apps into app stores.

Because offline support is more straightforward.

The only offline capability on the web at all is via Service Workers. They are really cool, but I might argue that they aren’t particularly easy to implement, especially if the plan is using them to offer truly offline experiences for a web app that otherwise heavily relies on the network.

Native apps are less reliant on the network for everything. The core assets that make the app work are already available locally. So if a native app doesn’t need the network (say, a game), it works offline just fine. Even if it does need the network to be at its best, having your last-saved data available seems like a typical approach that many apps take.

Potential Solution: Make building offline web apps easier.

I’m a web guy and I feel like building for the web is the smart play for the vast majority of “app” situations. But I gotta admit the list of reasons for a business to go for a native app is thick enough right now that it’s no surprise that many of them do. The most successful seem to do both, despite the extreme cost of maintaining both. Like responsive design was successful in preventing us from having to build multiple versions of a website, I hope this complex situation moves in the direction of having websites be the obvious and only choice for any type of app.


, , , , ,

Creating Scheduled Push Notifications

Scheduled is the key word there — that’s a fairly new thing! When a push notification is scheduled (i.e. “Take your pill” or “You’ve got a flight in 3 hours”) that means it can be shown to the user even if they’ve gone offline. That’s an improvement from the past where push notification required the user being online. 

So how do scheduled push notifications work? There are four key parts we’re going to look at:

  • Registering a Service Worker
  • Adding and removing scheduled push notifications
  • Enhancing push notifications with action buttons
  • Handling push notifications in the Service Worker

First, a little background

Push notifications are a great way to inform site users that something important has happened and that they might want to open our (web) app again. With the Notifications API — in combination with the Push API and the HTTP Web Push Protocol — the web became an easy way to send a push notification from a server to an application and display it on a device.

You may have already seen this sort of thing evolve. For example, how often do you see some sort of alert to accept notifications from a website? While browser vendors are already working on solutions to make that less annoying (both Firefox and Chrome have outlined plans), Chrome 80 just started an origin trial for the new Notification Trigger API, which lets us create notifications triggered by different events rather than a server push alone. For now, however, time-based triggers are the only supported events we have. But other events, like geolocation-based triggers, are already planned.

Scheduling an event in JavaScript is pretty easy, but there is one problem. In our push notification scenario, we can’t be sure the application is running at the exact moment we want to show the notification. This means that we can’t just schedule it on an application layer. Instead, we’ll need to do it on a Service Worker level. That’s where the new API comes into play.

The Notification Trigger API is in an early feedback phase. You need to enable the #enable-experimental-web-platform-features flag in Chrome or you should register your application for an origin trial.

Also, the Service Worker API requires a secure connection over HTTPS. So, if you try it out on your machine, you’ll need to ensure that it’s served over HTTPS.

Setting things up

I created a very basic setup. We have one application.js file, one index.html file, and one service-worker.js file, as well as a couple of image assets.

/project-folder ├── index.html ├── application.js ├── service-worker.js └── assets    ├─ badge.png    └── icon.png

You can find the full example of a basic Notification Trigger API demo on GitHub.

Registering a Service Worker

First, we need to register a Service Worker. For now, it will do nothing but log that the registration was successful.

// service-worker.js // listen to the install event self.addEventListener('install', event => console.log('ServiceWorker installed'));
<!-- index.html --> <script>   if ('serviceWorker' in navigator) {     navigator.serviceWorker.register('/service-worker.js');   } </script>

Setting up the push notification

Inside our application, we need to ask for the user’s permission to show notifications. From there, we’ll get our Service Worker registration and register a new notification for this scope. So far, nothing new.

The cool part is the new showTrigger property. This lets us define the conditions for displaying a notification. For now, we want to add a new TimestampTrigger, which accepts a timestamp. And since everything happens directly on the device, it also works offline.

// application.js document.querySelector('#notification-button').onclick = async () => {   const reg = await navigator.serviceWorker.getRegistration();   Notification.requestPermission().then(permission => {     if (permission !== 'granted') {       alert('you need to allow push notifications');     } else {       const timestamp = new Date().getTime() + 5 * 1000; // now plus 5000ms       reg.showNotification(         'Demo Push Notification',         {           tag: timestamp, // a unique ID           body: 'Hello World', // content of the push notification           showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification           data: {             url: window.location.href, // pass the current url to the notification           },           badge: './assets/badge.png',           icon: './assets/icon.png',         }       );     }   }); };

Handling the notification

Right now, the notification should show up at the specified timestamp. But now we need a way to interact with it, and that’s where we need the Service Worker notificationclick and notificationclose events.

Both events listen to the relevant interactions and both can use the full potential of the Service Worker. For example, we could open a new window:

// service-worker.js self.addEventListener('notificationclick', event => {   event.waitUntil(self.clients.openWindow('/')); });

That’s a pretty straightforward example. But with the power of the Service Worker, we can do a lot more. Let’s check if the required window is already open and only open a new one if it isn’t.

// service-worker.js self.addEventListener('notificationclick', event => {   event.waitUntil(self.clients.matchAll().then(clients => {     if (clients.length){ // check if at least one tab is already open       clients[0].focus();     } else {       self.clients.openWindow('/');     }   })); });

Notification actions

Another great way to facilitate interaction with users is to add predefined actions to the notifications. We could, for example, let them choose if they want to dismiss the notification or open the app.

// application.js reg.showNotification(   'Demo Push Notification',   {     tag: timestamp, // a unique ID     body: 'Hello World', // content of the push notification     showTrigger: new TimestampTrigger(timestamp), // set the time for the push notification     data: {       url: window.location.href, // pass the current url to the notification     },     badge: './assets/badge.png',     icon: './assets/icon.png',     actions: [       {         action: 'open',         title: 'Open app’       },       {         action: 'close',         title: 'Close notification',       }     ]   } );

Now we use those notifications inside the Service Worker.

// service-worker.js self.addEventListener('notificationclick', event => {   if (event.action === 'close') {     event.notification.close();   } else {     self.clients.openWindow('/');   } });

Cancelling push notifications

It’s also possible to cancel pending notifications. In this case, we need to get all pending notifications from the Service Worker and then close them before they are sent to the device.

// application.js document.querySelector('#notification-cancel').onclick = async () => {   const reg = await navigator.serviceWorker.getRegistration();   const notifications = await reg.getNotifications({     includeTriggered: true   });   notifications.forEach(notification => notification.close());   alert(`$ {notifications.length} notification(s) cancelled`); };


The last step is to set up the communication between the app and the Service Worker using the postMessage method on the Service Worker clients. Let’s say we want to notify the tab that’s already active that a push notification click happened.

// service-worker.js self.addEventListener('notificationclick', event => {   event.waitUntil(self.clients.matchAll().then(clients => {     if(clients.length){ // check if at least one tab is already open       clients[0].focus();       clients[0].postMessage('Push notification clicked!');     } else {       self.clients.openWindow('/');     }   })); });
// application.js navigator.serviceWorker.addEventListener('message', event => console.log(event.data));


The Notification API is a very powerful feature to enhance the mobile experience of web applications. Thanks to the arrival of  the Notification Trigger API, it just got a very important improvement. The API is still under development, so now is the perfect time to play around with it and give feedback to the developers.

If you are working with Vue or React, I’d recommend you take a look at my own Progressive Web App demo. It includes a documented example using the Notification Trigger API for both frameworks that looks like this:

The post Creating Scheduled Push Notifications appeared first on CSS-Tricks.


, , ,

Push and ye shall receive

Sometimes the seesaw of web tech is fascinating. Service workers have arrived, and beyond offline networking (read Jeremy’s book) which is possibly their best feature, they can enable push notifications via the Push API.

I totally get the push (pun intended) to make that happen. There is an omnipresent sentiment that we want the web to win, as there should be in this industry. Losing on the web means losing to native apps on all the different platforms out there. Native apps aren’t evil or anything — they are merely competitive and exclusionary in a way the web isn’t. Making the web a viable platform for any type of “app” is a win for us and a win for humans.

One of the things native apps do well is push notifications which gives them a competitive advantage. Some developers choose native for stuff like that. But now that we actually have them on the web, there is pushback from the community and even from the browsers themselves. Firefox supports them, then rolled out a user setting to entirely block them.

We’re seeing articles like Moses Kim’s Don’t @ me:

Push notifications are a classic example of good UX intentions gone bad because we know no bounds.

Very few people are singing the praises of push notifications. And yet! Jeremy Keith wrote up a great experiment by Sebastiaan Andeweg. Rather than an obnoxious and intrusive push notification…

Here’s what Sebastiaan wanted to investigate: what if that last step weren’t so intrusive? Here’s the alternate flow he wanted to test:

  1. A website prompts the user for permission to send push notifications.
  2. The user grants permission.
  3. A whole lot of complicated stuff happens behinds the scenes.
  4. Next time the website publishes something relevant, it fires a push message containing the details of the new URL.
  5. The user’s service worker receives the push message (even if the site isn’t open).
  6. The service worker fetches the contents of the URL provided in the push message and caches the page. Silently.

It worked.

Imagine a PWA podcast app that works offline and silently receives and caches new podcasts. Sweet. Now we need a permissions model that allows for silent notifications.

The post Push and ye shall receive appeared first on CSS-Tricks.


, ,