Commit b2fb3fc
fix(app): polyfill requestIdleCallback for iOS Safari (#5816)
## Summary
- iPhone users could not use the app: every iOS Safari (and CriOS, which
uses WebKit) instance threw `TypeError: window.requestIdleCallback is
not a function` at `AppDataProvider` mount, which `ErrorBoundary` caught
— leaving the user stuck on the error fallback. Surfaced by the
on-device debug console added in #5808.
- `requestIdleCallback` is documented as unsupported on Safari/iOS by
[caniuse](https://caniuse.com/requestidlecallback),
[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback),
and [WebKit bug 164193](https://bugs.webkit.org/show_bug.cgi?id=164193).
It exists only behind the *Experimental Features* toggle, which is off
by default — so this is a hard regression for **every** iOS visitor, not
a one-off device bug.
- Fix is an inline feature-detect in the existing `useEffect`: use
`requestIdleCallback` if present, otherwise fall back to `setTimeout(cb,
1)`. Cleanup picks the matching cancel function via captured boolean. No
new npm dependency, no global mutation.
- Adds a regression test that stubs both idle APIs as `undefined` and
asserts the three initial fetches still fire and children still render.
### Original iPhone error report
```
Message: window.requestIdleCallback is not a function. (In 'window.requestIdleCallback(async()=>{try{let[e,t,n]=await Promise.all([fetch(`${T}/specs`),fetch(`${T}/libraries`),fetch(`${T}/stats`)]);...},{timeout:2e3})')
URL: https://anyplot.ai/?debug=1
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 26_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/148.0.7778.100 Mobile/15E148 Safari/604.1
```
## Test plan
- [x] `cd app && yarn test --run` — 467/467 pass, including new `falls
back to setTimeout when requestIdleCallback is unavailable (iOS Safari)`
case in `Layout.test.tsx`.
- [x] `cd app && yarn build` — TypeScript / Vite build green.
- [x] Pre-existing lint findings on `main` unchanged; no new findings
introduced by this PR.
- [ ] Production verification on the reporter's iPhone after deploy
(visit https://anyplot.ai/?debug=1, confirm home page loads and the
Eruda console is clean).
## Why this approach (not a polyfill package)
- Two well-known polyfills exist
([aFarkas/requestIdleCallback](https://github.com/aFarkas/requestIdleCallback),
[pladaria/requestidlecallback-polyfill](https://github.com/pladaria/requestidlecallback-polyfill))
but both internally just wrap `setTimeout` — there is no way to truly
polyfill real browser idle time. Adding a dependency would ship bytes to
every modern-browser user for the same effect we get inline.
- Single call site in the entire repo — per CLAUDE.md "no abstractions
beyond what the task requires", a per-file inline fix is the right
shape. If a second call site appears later, that is the moment to
extract a `utils/idleCallback.ts`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 7a09315 commit b2fb3fc
2 files changed
Lines changed: 43 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
130 | 158 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
42 | 44 | | |
43 | | - | |
| 45 | + | |
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| |||
65 | 67 | | |
66 | 68 | | |
67 | 69 | | |
68 | | - | |
69 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
70 | 81 | | |
71 | 82 | | |
72 | 83 | | |
| |||
0 commit comments