Skip to content

PWA support

@flyze/lib-core-angular 1.0.0-alpha.34· latest

PwaModule wraps @angular/service-worker to detect that an app is running as an installed PWA, notice that a new version is ready, and activate it.

Read the limitations section before relying on this module. The update path works; the install path does not, and the timing constants are wrong.

PwaModule.forRoot(config) provides PwaService, PwaUpdateService and the config under the PwaConfigService token (pwa.module.ts:16). A @Optional() @SkipSelf() constructor guard throws if the module is imported more than once (pwa.module.ts:27) — importing without forRoot() provides nothing and injection fails instead.

interface PwaConfig {
update: { auto: boolean; needsUserPermission: boolean };
}

PwaService.initialize() must be called explicitly — nothing happens on injection (pwa.service.ts:35). It resolves appIsPwa, starts network tracking, and — only when running as a PWA — starts update checks and the install listeners.

PwaUpdateService.appIsPwa() returns SwUpdate.isEnabled (pwa-update.service.ts:58), which is true when a service worker is registered and controlling the page. That is a proxy, not the real question: a browser tab of a service-worker-enabled app reports true while an installed app whose worker has not taken control yet reports false. It says “the service worker is active”, not “the app was installed”.

checkForUpdates() subscribes to SwUpdate.versionUpdates, filtered to VERSION_READY (pwa-update.service.ts:34). On such an event updateAvailable$ emits true, and then the config decides:

autoneedsUserPermissionBehavior
falsenothing — updateAvailable$ only
truefalseactivate and reload immediately
truetrueask first — but see the limitations

promptUser() calls activateUpdate() and then document.location.reload() (pwa-update.service.ts:78). Note the flag names read backwards from what they do: needsUserPermission: false is the silent path.

Observables: updateAvailable$ and updated$ on PwaUpdateService, installAvailable$, installed$ and networkAvailable$ on PwaService.

All of these are in shipped code today.

  • The install prompt never fires. The listeners are registered for 'window:beforeinstallprompt' and 'window:appinstalled' (pwa.service.ts:68) — window: is Angular’s @HostListener prefix, not part of the DOM event name, which is beforeinstallprompt. So installPrompt is never captured, installAvailable$ never emits true, and install() can never do anything.
  • installed$ is the wrong subject. It is assigned installAvailableSubject$.asObservable() (pwa.service.ts:26), so it mirrors install-availability and installedSubject$ is written but never read.
  • The periodic update check runs every 21.6 seconds. interval(6 * 60 * 60) (pwa-update.service.ts:28) is milliseconds — the intended six hours needs * 1000. It also runs unconditionally from the constructor, is never unsubscribed, and is independent of checkForUpdates().
  • needsUserPermission: true silently never updates. allowUpdateModal is an empty method with its body commented out (pwa-update.service.ts:62), left over from a removed dialog component. The callback is never invoked, so no update is ever activated in that mode. MatDialog is still injected and unused.
  • catchError used as a promise handler. activateUpdate().then(onSuccess, catchError(…)) (pwa-update.service.ts:79) passes an RxJS operator where a rejection handler belongs, so a failed activation does not run the intended reset.
  • Duplicate network tracking. checkNetworkAvailable() reimplements NetworkStatusService (network-status) with a hardcoded true seed and no duplicate suppression.
  • updated$ and updateAvailable$ both emit true after a successful activation (pwa-update.service.ts:81), which reads like a copy-paste of the failure branch.

Practical guidance: use updateAvailable$ plus auto: true, needsUserPermission: false, or drive SwUpdate directly. Use network-status for connectivity, and do not build an install button on this module.