Skip to content

feat(ios): extend Apple GPU detection with missing chips + iOS version filter#158

Merged
puckey merged 3 commits intomasterfrom
feat/ios-detection
Apr 14, 2026
Merged

feat(ios): extend Apple GPU detection with missing chips + iOS version filter#158
puckey merged 3 commits intomasterfrom
feat/ios-detection

Conversation

@puckey
Copy link
Copy Markdown
Collaborator

@puckey puckey commented Apr 14, 2026

Summary

Builds on @HarshdeepKahlon's #146 with UA-aware iOS version detection, chipset-candidate filtering, a few corrections on the chip additions, a small perf win, and one dep drop.

Commits

  1. 97b23ff / 700b0f1@HarshdeepKahlon's chipset additions (A18 / A18 Pro / A19 / A19 Pro on iPhone; M3 / M4 / M5 on iPad), picked from add apple m3, m4, m5, a18 + pro, a19 + pro #146.
  2. 9b21de2 — our combined feature commit.

What's in the combined commit

1. Chipset corrections

  • Rename the iPhone A17 entry to a17 pro. Apple only ever shipped A17 Pro, and apple a17 gpu did not exist in any benchmark file, so the previous entry emitted a string that never matched.
  • Add A16 (2025 iPad Air / iPad 11) and A17 Pro (iPad mini 7) to the iPad chipset list.
  • Drop FALLBACK test cases for A19 / A19 Pro / M5 — all three are present in the benchmark JSON and correctly resolve to BENCHMARK.

2. UA-aware chipset filter

On iOS Safari the WebGL renderer is masked to Apple GPU. detect-gpu works around this with a fragment-shader pixel-ID trick that yields a pool of candidate chips; the benchmark matcher picks one by screen-size distance. The candidate pool previously included every chip that could theoretically run iOS 14+ — far wider than necessary.

This PR parses the iOS major version from the UA and drops chips the device cannot run:

OS Min iPhone chip Min iPad chip
iOS/iPadOS 13–15 A9 A8
iOS/iPadOS 16 A10 A9
iOS/iPadOS 17–18 A12 A10
iOS/iPadOS 26 A13 A12

3. UA parsing edge cases

  • iOS 26+ freezes the iPhone OS 18_6 token as a fingerprinting countermeasure. The real iOS major is exposed only via Version/N. The parser prefers Version/ whenever it disagrees upward with the OS token.
  • Pre-iOS-7 Safari Version/ tracked Safari, not iOS (Safari 4 on iOS 3.2, Safari 5 on iOS 4). Falls back to the OS token when OS < 7.
  • iPadOS "Request Desktop Site" UAs are Mozilla/5.0 (Macintosh; ...) with no iPhone/iPad token. The parser still fires because isIpad is true via MacIntel + maxTouchPoints > 0.
  • In-app browsers (Facebook, Slack, Snapchat, etc.) hide the iOS version in proprietary tokens. These yield undefined → the filter becomes a safe no-op.

4. Pixel-ID shader short-circuit on iOS 14+

Apple normalized the shader output in iOS 14, so all modern devices return the same codeFB — which the old code used purely as a "this is iOS 14+" signal. The UA now provides that signal directly, without the shader compile + draw + readPixels round-trip. The shader path is retained as the fallback for iOS 12.x / 13.x devices, where the three distinct pixel IDs still discriminate A7 vs A8–A10 vs A11+ within a single iOS version.

5. Drop webgl-constants dependency

The shader function used 9 WebGL enum constants (GL_ARRAY_BUFFER, GL_FLOAT, etc.) from the webgl-constants package. Inlined them as module-local const declarations with a Khronos spec link; minifier gets the same literal folding, one fewer runtime dependency.

Tests

  • test/deobfuscateAppleGPU.test.ts — 11 unit tests for filterByIOSVersion, covering every cutoff and the M-series passthrough.
  • test/deviceInfo.test.ts — 6 UA-shape tests for parseIOSVersion via a stubbed navigator.
  • test/iosUserAgents.test.ts — runs parseIOSVersion against a 129-entry iOS UA corpus (iOS 1–18) sourced from ua-parser/uap-core's test_os.yaml, plus explicit pre-iOS-7 / iOS 26 freeze / separator-variant tests. Asserts the parser never returns a wrong major and recognizes 100% of UAs with one of the two standard tokens.
  • test/fixtures/ios-user-agents.json — the 129-entry corpus.

All 1666 tests pass; lint, format, build, and typecheck clean. Bundle: 7.65 kB / 3.61 kB gzip.

Suggested release bump

Adds new behavior (filtering) and a new exported helper (parseIOSVersion). Suggest a minor bump when triggering the release workflow.

Test plan

  • pnpm test — 1666 tests pass
  • pnpm lint passes
  • pnpm format:check passes
  • pnpm build succeeds
  • CI confirms the same

@puckey puckey force-pushed the feat/ios-detection branch 6 times, most recently from d576de6 to ab79f54 Compare April 14, 2026 13:35
Builds on the chip-list additions (A18/A18 Pro/A19/A19 Pro; M3/M4/M5)
with five changes:

1. **A17 Pro rename + missing iPad chips.** Apple only ever shipped A17
   Pro, so the previous iPhone `a17` entry emitted `apple a17 gpu` which
   never matched any benchmark row. Rename to `a17 pro`. Also add A16
   (2025 iPad Air / iPad 11) and A17 Pro (iPad mini 7) to the iPad
   chipset list. Drop FALLBACK test cases for A19 / A19 Pro / M5 — all
   three are now present in the benchmark JSON and correctly resolve to
   BENCHMARK.

2. **UA-aware iOS version parsing and chipset filter.** parseIOSVersion
   extracts the iOS major from the UA. The deobfuscator then drops
   A-series chips the device cannot run, narrowing the candidate pool
   before benchmark matching:

       iOS/iPadOS 13-15   iPhone A9+   iPad A8+
       iOS/iPadOS 16      iPhone A10+  iPad A9+
       iOS/iPadOS 17-18   iPhone A12+  iPad A10+
       iOS/iPadOS 26      iPhone A13+  iPad A12+ (Neural Engine)

   Two non-obvious UA quirks are handled:
     - iOS 26+ freezes the legacy `iPhone OS 18_6` token for
       fingerprinting reduction; the true OS major is exposed only via
       Safari's `Version/N` token. parseIOSVersion prefers Version/
       whenever it disagrees upward with the OS token.
     - Pre-iOS-7 Safari `Version/` tracked Safari (then 4 or 5), not
       iOS. Falls back to the OS token when OS < 7.
     - iPadOS "Request Desktop Site" UAs (`Macintosh; Intel Mac OS X`)
       still resolve because `isIpad` is true via the
       `MacIntel + maxTouchPoints > 0` detection.
     - In-app browsers (Facebook, Slack, Snapchat) hide the iOS version
       in proprietary tokens. They yield `undefined`, making the filter
       a safe no-op — preferred over guessing wrong.

3. **Skip the pixel-ID shader on iOS 14+.** Apple normalized the shader
   output in iOS 14, so every modern device returns the same `codeFB` —
   which the pre-existing code was using purely as an "is iOS 14+"
   signal. The UA now provides that directly, without the shader
   compile + draw + readPixels round-trip. The shader path is retained
   as the fallback for iOS 12.x / 13.x devices where the three distinct
   pixel IDs still discriminate A7 vs A8-A10 vs A11+ within a single
   iOS major.

4. **Drop webgl-constants dep.** Inline the 9 WebGL enum values the
   shader needs as module-local consts with a link to the Khronos spec.
   Same minifier-friendly literals, one fewer runtime dependency.

5. **Tests.** 24 new tests:
     - `test/deobfuscateAppleGPU.test.ts` — 11 unit tests for
       filterByIOSVersion, covering every cutoff and the M-series
       passthrough.
     - `test/deviceInfo.test.ts` — 6 UA-shape tests for parseIOSVersion
       via a stubbed navigator.
     - `test/iosUserAgents.test.ts` — runs parseIOSVersion against a
       129-entry iOS UA corpus (iOS 1-18) sourced from ua-parser/
       uap-core's test_os.yaml, plus explicit pre-iOS-7 / iOS 26 freeze
       / separator-variant tests. Asserts the parser never returns a
       wrong major and recognizes 100% of UAs with one of the two
       standard tokens.
@puckey puckey force-pushed the feat/ios-detection branch from eb8abd2 to 9b21de2 Compare April 14, 2026 13:42
@puckey puckey merged commit bdd1862 into master Apr 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants