# iOS 添加到主屏幕

> web 应用如何经共享菜单进入 iOS 主屏幕，iOS 16.4 与 26 上由什么决定以 web 应用还是书签打开，以及页面如何检测 standalone。

在 iOS 与 iPadOS 上，唯一的安装路径是用户自己的手势：共享，然后"添加到主屏幕"。Safari 从 iPhone OS 1 起就提供它；第三方浏览器自 iOS 16.4 起也可以提供；没有任何版本会触发 `beforeinstallprompt` 或暴露安装 API（BCD `api.BeforeInstallPromptEvent`），所以页面负责提供图标、名称与 manifest，而拿着设备的人决定图标如何启动，在 iOS 26 上这是一个明确的 "Open as Web App" 开关。

## 工作原理

手势是固定的：点按**共享**，点按**添加到主屏幕**，可选地修改名称，点按**添加**。自 iOS 16.4 起，第三方浏览器在满足以下条件时可以提供同一菜单项：持有 `com.apple.developer.web-browser` 权限、把一个正在显示 HTTP 或 HTTPS 文档的 `WKWebView` 放入共享菜单的 `activityItems`，且设备不是共享 iPad（WebKit，"Web Push for Web Apps on iOS and iPadOS"）。

点按图标后发生什么，在 16.4 与 26 之间发生了变化：

| iOS 版本 | 以 web 应用方式打开（独立的应用切换器条目、无 Safari 界面）的条件 | 否则 |
|---|---|---|
| 16.4 至 18 | manifest 的 `display` 为 `standalone` 或 `fullscreen`，或旧的 `apple-mobile-web-app-capable` meta 标签为 `yes`；用哪个浏览器添加无关 | 保存为主屏幕书签，在默认浏览器中打开 |
| 26 | 默认对每个添加的站点都成立；用户可以在添加面板中关闭 "Open as Web App"，即使站点已配置为 web 应用 | 书签，仅当用户关闭了该开关 |

WebKit 对 26 的表述是：Safari 没有可安装性要求，不需要 manifest、不需要 service worker、除页面本身能加载外不检查 HTTPS。manifest 仍决定 web 应用的样子：`icons` 自 iOS 15.4 起被读取，`name` 与 `short_name` 作为可编辑标题的初始值，`id`（16.4+）与用户选定的名称组合，使同一站点的两次安装（"Shiny (work)" 与 "Shiny (personal)"）保持可区分，专注模式也能分别处理。同一站点多次安装是受支持且有意为之的。

其余由标记提供，优先级如下：

- 图标：同时存在时 `<link rel="apple-touch-icon" sizes="180x180" href="…">` 优先于 manifest 的 `icons`；Apple 的归档指南记载的选取顺序是最接近设备尺寸的图标、其次是比它大的最小图标、最后是最大的图标。完全没有图标时，iOS 16.4 及以后用站点名称首字母加站点取色生成一个字母图标。
- 名称：`<meta name="apple-mobile-web-app-title" content="…">`，否则取 `<title>`，用户添加前可编辑。
- 启动图：默认为上次启动的截图；`<link rel="apple-touch-startup-image" href="…">` 覆盖它，离线时尤其重要。
- 状态栏：`<meta name="apple-mobile-web-app-status-bar-style" content="black">` 仅在 standalone 模式下生效；Apple 归档指南写明它 "has no effect unless you first specify standalone mode"。

进入主屏幕后，web 应用获得 Safari 标签页没有的能力：Web Push（16.4+，需用户手势；18.4 的 Declarative Web Push 通过 `window.pushManager` 订阅，无需 service worker）、Badging API（16.4+，允许通知后绘制）、Screen Wake Lock、Screen Orientation 与 User Activation（均为 WebKit 在 16.4 中列出）。WebKit 在任何 iOS 版本中都没有要求主屏幕条目具备 service worker（WebKit，Safari 26.0 文章）。存储仍是尽力而为：开启跨站跟踪防护时，Safari 会删除在 7 天浏览器使用中没有任何交互的源所写入的脚本数据，`navigator.storage.persist()` 根据交互历史自动回答、不弹提示（MDN，存储配额与清除）。

## 实测行为

下面的行为是读者可以在设备上复现的；页面无法观察安装本身，只能在事后观察显示上下文。

:::observed
在运行 iOS 26 的 iPhone 上（英文界面），Safari 共享菜单列出 "Add to Home Screen"；下一屏显示可编辑的名称与一个默认开启的 "Open as Web App" 开关，关闭它则保存为书签（WebKit，"WebKit features in Safari 26.0"）。在启动后的 web 应用内，`navigator.standalone` 为 `true`，`matchMedia('(display-mode: standalone)').matches` 为 `true`；同一设备的 Safari 标签页中 `navigator.standalone` 为 `false`，Android 上的 Chrome 中该属性为 `undefined`。
:::

由于不存在安装事件，唯一有用的分支是显示上下文，用标准媒体特性判断，并以旧属性作为老版本 iOS 的回退：

```js
const standalone =
  matchMedia('(display-mode: standalone)').matches ||
  navigator.standalone === true;              // 仅 Safari 的旧属性

if (standalone) {
  hideInstallHint();                          // 从主屏幕启动
} else if (/iPhone|iPad/.test(navigator.userAgent)) {
  showShareMenuInstructions();                // "点按共享，然后添加到主屏幕"
}
```

在第二次及之后的访问才显示提示，用触达换相关性；在已启动的 web 应用内显示提示则是噪音，上面的分支避免了这一点。user agent 判断只用于挑选说明文字；能力从 `display-mode` 读取。

## 另请参阅

- [WebKit features in Safari 26.0](https://webkit.org/blog/17333/webkit-features-in-safari-26-0/)（webkit.org）
- [Web Push for Web Apps on iOS and iPadOS](https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/)（webkit.org）
- [Configuring Web Applications](https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html)（developer.apple.com）
- [iOS 与 Safari 上的 PWA](/zh/reference/platforms/ios-safari/)
- [iOS Safari 推送](/zh/reference/notifications/ios-safari-push/)
- [Badging API](/zh/reference/installation/badging/)
- [`id` manifest 成员](/zh/reference/manifest/id/)
- [可安装性条件](/zh/reference/installation/installability-criteria/)