iOS PWA Firebase and Storage blocks - can't find issue

Hi there

I have been struggling getting an iOS PWA app using Angular 10 with service worker out of the box working correctly.

After a while there seems to be two things happening which I cannot trace back to a cause.

  • storage sets are blocking or not completing - concurrency happening? I am doing storage.ready check. I could think of a concurrency happening because of some/many sequential read/writes, but then again, if I add delays, it is still block (yes I know - setTimeout are sign of async misunderstanding on my end)

  • I am getting firebase network errors (ā€˜channel’ of xhr requests to firestore.googleapis.com), which seemingly also creates blocking calls (e.g. batch.commit). I was considering bypassing angular service worker for calls to firebase API endpoints, but using dataGroups I may do things incorrectly.

On Android and Browser things look nicely (Win10).

Anyone experienced same debugging hell and know what is the catch I am missing here?

FYI, the app is supposed to be an offline first app which does some syncing at login/logout stage. For a number of collections stores to localstorage (Ionic Storage - driver: ā€˜indexeddb’) and posting/getting data from Firebase.

1 Like

Some more information that might be useful:

  • was the PWA set up using the standard Angular ng add method, or did you PWAize the app manually?
  • are you managing the ā€œoffline/onlineā€ state somehow in your app code? if so, how?
  • is Firestore’s persistence mode involved here, and if so, is it enabled as needed?
  • can you attach a debugger and instruct it to break on any uncaught JavaScript exceptions?
  • can you reproduce the problem using ordinary Safari?
  • what version of mobile Safari is demonstrating the problem? This reference for using IndexedDB from service workers suggests that Safari 10 may be buggy in this regard.
  • do the errors occur when the PWA is always online? only when it is offline? when it has gone offline and has now come back online?

Thx for responding.

I am using PWA out of the box using ng add - no modifications to config. The issues occur while in online state (It is not a pure offline first app, as the user needs to authenticate using Facebook/Google). The offline features are there to avoid bandwidth usage where not needed.

Persistence mode was off and now on, not affecting the bug. This persistence mode is notably not suited for storage over longer time (only to compensate for flaky connections). At first, I was hoping this solved the network issue. I keep it on for now.

I am tracing using Safari Tech Preview in Catalina. Software version 14.4 on iOS.

I am not sure how both are related, if at all, or if there is something else going on causing both to fail. Or maybe, a first thing fails, creating some sort of inconsistent state in the browser.

This error occurs after first run through my own home-brew routine:

Which relates to a number of XHR requests hanging and then reporting error:

After this error simple storage.sets are not completing anymore. Before the error occurs, writes/reads do work. So I suspect this could be pointing towards the begin of the issue.

I think I might need to focus on this error first. While maybe at first unrelated, I think under the hood Firebase is using local storage.

Some theories I have about why things may go wrong:

  • concurrent writes/reads in my own load/save routine?
  • too many reads/writes to local within a timeframe (throttling)
  • service worker handling the firebase XHR incorrectly?
  • localstorage faulting because network is having issues
    …

I have tried to google on the FetchEvent error and PWA, or Firebase, but haven’t seen something related.

I am struggling to understand how to configure the service worker to not handle requests to firestore.googleapis.com. That could be an interesting experiment, even though other requests do pass through. So I wonder if that will pinpoint the issue.

As to the local storage, I have remove the local loading in storage, so an online-only version, but then the network error persists. (I would have to check again, btw - yesterday I tested many options)

The vast majority of cases I run into that have this sort of shape are caused by uncaught exceptions: periodically-scheduled things work great until some unusual condition occurs, at which point they just die. ā€œMultitaskingā€ in JavaScript fundamentally consists of a cooperative set of scheduled timer events: do something, schedule with setTimeout another call to this function which does it all over again. When there’s an uncaught error during a ā€œdo somethingā€, the next setTimeout never gets posted and the whole house of cards comes down.

You’ve seen a version of this probably a million times when a template references a controller subproperty of a property that is not yet defined: {{user.id}} when user is populated asynchronously. It manifests as ā€œall the styling on all my components is brokenā€, because the code that would have done all that styling got short-circuited by the ā€œcan’t reference property of undefinedā€ error that isn’t caught.

So I’m imagining that there is some thread that’s sitting around polling the network state that gets torpedoed by something unexpected happening in it, and it just dies at that point. Maybe it’s intended to be running things in the Angular zone and isn’t: this can be a problem when your code gets triggered events from non-Angular-aware libraries.

Definitely try breaking on uncaught exceptions.

Thanks for this. Giving some inspiration to review the things happening earlier on.

As to seeing exceptions: other then what can be found through try-catch elements, catch in promises and/or error handlers in subscribe, I rely on the excptions posted in error logs. I reckon that is all I can look at (not yet considered looking at the iOS version of adb logcat).

I’m far from my Mac at the moment, but I would think Safari has something similar to this, but in Chrome’s developer tools, right above the stack trace panel, there are a bunch of buttons for pause, step over, into, out, etc. The rightmost one of those looks like a pause button in an octagon. Click it to break on any uncaught exceptions, and then underneath it there should be an additional checkbox that will break even on caught exceptions.

1 Like

Found the option in Safari. Let’s see what I can find.

Just a quick catch-up

The uncaught exception breakpoint yields an error on something obscure:

CandidateElement.extraArticleCandidateIfElementIsViable

image

Something related to Safari-reader.js or safari.js…

Let’s continue the search…

Yeah, don’t turn it on right away. Allow the app to settle. There will be some false positives when Angular internals poke around as to what polyfills are needed for its bootstrapping.

For now I am going to tear down some things and see how to get things stabilised a bit more while we can continue user tests. The whole offline/online thing I have built - I think I need to refactor it.

Also switching off the service worker - for now not key.

Further simplification of the internals - like writes and such to storage did not help. Switching off service worker neither. Still something odd happening between Firebase and Storage. Good thing thought this refactoring did speed up the performance (only 1 write instead of multiple) and cleaned the code.

Going to transplant the code to a new project and build it up again. Maybe leaving behind some legacy/incorrect dependencies. Already seeing that a full npm i fails after remove node_modules.

Some work to be done.

Finalised the rebuild in Angular 11 which did help a bit. Maybe the newer versions bring value or the rebuild cleaned up some items (including node_modules).

Still, sometimes the IndexedDB driver in iOS becomes unavailable - either because of reload/network failure or due to other actions.

Same here. I ma facing the same problems