ENGLISH·COURSE API
    Preparing search index...
    • Client hook: which Safari install flow, if any, this learner can be shown.

      Returns SafariInstallSurface

      The surface to guide, or "none"

      The iPhone answer comes from useCanInstallToHomeScreen, which already owns that question and has its own spec. What this adds is the pair the user agent cannot separate on its own.

      iPadOS 13 and later send a Macintosh user agent with no iPad token in it, so an iPad and a Mac are byte-for-byte identical here. navigator.maxTouchPoints is what tells them apart, and getting it wrong is not cosmetic: it serves one platform the other's guide, naming controls that are not on the learner's screen — the exact failure these guides exist to prevent.

      A feature test for touch events would not do. ontouchstart is present on plenty of desktop Chromium builds and absent on some touch hardware; maxTouchPoints is the signal that actually reads 0 on a Mac and 5 on an iPad. navigator.userAgentData would be better still, and Safari does not implement it.

      Everything it reads is a property of the browser, so it reports "none" until hydration commits — rendering anything else during hydration makes the server and client markup disagree — and "none" again once the app is running from the home screen, because then the learner has already done it.

      const surface = useSafariInstallSurface();

      if (surface === "none") return null;
      return <InstallAppButton surface={surface} />;
      • useCanInstallToHomeScreen
      • isRunningStandalone