跳转到内容

iOS 添加到主屏幕:iOS PWA 安装

发布于 更新于

一句话: 在 iOS 与 iPadOS 上,安装 PWA 的唯一途径是用户自己的手势——点按分享, 再点按“添加到主屏幕”。没有可挂接的安装事件,也没有你能触发的提示。你的标记提供的是默认值 与素材——图标、默认名称、清单——而名称由用户在添加时编辑;在 iOS 26 上,图标如何启动则由 用户的“以 Web 应用打开”开关决定。

自第一代 iPhone 起,用户就可以把任意网站添加到主屏幕。在 iOS 与 iPadOS 的 Safari 中, 流程是:点按分享按钮打开分享菜单,然后点按**“添加到主屏幕”**。该网站的图标随即出现在 主屏幕上,轻点一下就能回到站点。用户在添加时还可以修改应用的名称。

没有程序化的替代方案。beforeinstallprompt——其他平台在提示用户把站点安装到主屏幕之前 触发的那个事件——是非标准的,且被标注为可用性有限,因此这里没有任何东西可供拦截、延迟或 重新触发。

iOS 与 iPadOS 16.4 让第三方浏览器也能在分享菜单中提供“添加到主屏幕”。WebKit 明确记录了 该菜单项出现所需满足的条件:

  • 应用持有 com.apple.developer.web-browser 托管授权;
  • 分享菜单的 activityItems 数组中包含一个 WKWebView;
  • 该 WKWebView 正在显示 HTTP 或 HTTPS URL 的文档;
  • 若设备是 iPad,则它未被配置为共享 iPad。

用户点按生成的图标后会发生什么,在 16.4 与 26 之间发生了实质变化。

在 iOS 与 iPadOS 16.4 上,只要网站的清单把 display 成员设为 standalone 或 fullscreen,它就会以 Web 应用方式打开——无论是哪个浏览器添加的——并在 App 切换器中 拥有独立于 Safari 的预览。如果既没有请求 Web 应用行为的清单,也没有把站点标记为 web-app-capable 的 meta 标签,该条目会保存为主屏幕书签;自 16.4 起,这类书签会在用户当前的 默认浏览器中打开。

在 iOS 26 与 iPadOS 26 上,WebKit 修改了这一行为:默认情况下,添加到主屏幕的每一个 网站都以 Web 应用方式打开。更想要浏览器书签的用户,可以在添加时关闭“以 Web 应用打开”—— 即便站点本身被配置为 Web 应用。WebKit 的表述很直白:Safari 中现在对“可安装性”已没有任何 要求,用户可以把任意站点添加到主屏幕并以 Web 应用方式打开它。决定权从你的标记转移到了 拿着手机的那个人手里。

这并没有移除任何支持。WebKit 明确指出,清单带来的好处依然生效——在清单中声明图标,它们 就会被使用——只是让用户获得 Web 应用体验不再需要清单文件;正如 iOS 与 iPadOS 的主屏幕 Web 应用从来不像其他平台的 PWA 那样要求 Service Worker。

  • 清单图标自 iOS 与 iPadOS 15.4 起即受支持。 你也可以沿用在文档头中列出 apple-touch-icon 链接这一长期做法——如果两者都提供,apple-touch-icon 优先于 清单声明的图标。
  • 不提供图标,系统会替你造一个。 过去 iOS 会用站点截图生成图标;16.4 改为用站点名称的 首字母加上取自站点的颜色生成一个字母图标。
  • 为触摸图标设定尺寸。 Apple 的指南建议把 apple-touch-icon.png 放在根文档目录作为 全站图标,或用 sizes 声明分页面图标;系统使用最贴近设备合适尺寸的图标,其次是大于该 尺寸中最小的那个,再次是可用图标中最大的那个。
  • 启动图。 默认情况下,应用启动时显示的是它上次启动时的截图; <link rel="apple-touch-startup-image" href="/launch.png"> 可覆盖它。这在 Web 应用离线时 尤其有用。
  • 启动图标标题。 默认使用 <title> 标签; <meta name="apple-mobile-web-app-title" content="AppTitle"> 可设置不同的名称。
<!-- 全站图标,以及一个针对设备的覆盖项 -->
<link rel="apple-touch-icon" href="/touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="180x180" href="/touch-icon-iphone-retina.png">
<!-- Web 应用启动时显示的启动图 -->
<link rel="apple-touch-startup-image" href="/launch.png">
<!-- 主屏幕图标下方的名称 -->
<meta name="apple-mobile-web-app-title" content="AppTitle">

Apple 的存档指南描述了清单出现之前的机制。理解它仍有价值,因为你会在旧代码和旧建议中 遇到它:

把 apple-mobile-web-app-capable meta 标签设为 yes 以开启独立模式。

在那个存档模型中,独立模式意味着不使用 Safari 来显示网页内容——顶部没有 URL 输入框、 底部没有按钮栏,只保留状态栏——并且 apple-mobile-web-app-status-bar-style “除非你先按上述方式指定独立模式,否则不起作用”。WebKit 自己的历史记述把这个标签定位在 2008 年 8 月的 iPhone OS 2.1;Web 应用清单自 2013 年开始标准化,Safari 于 2018 年 3 月 随 iOS 11.4 采纳。

请把这层依赖关系当作有据可查的历史行为来读,而不是今天仍在决定独立模式的规则:在 iOS 26 与 iPadOS 26 上,决定主屏幕条目是否以 Web 应用打开的是“以 Web 应用打开”开关, 无论标记怎么写。

<!-- 旧版独立模式,如 Apple 存档指南所述 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- "black" 是 Apple 存档指南所演示的取值 -->

你无法询问用户能否安装——因为不存在安装事件。你能询问的是页面此刻以何种方式显示, 而这正是真正有意义的分支。可用的检查有两种:

  • window.navigator.standalone 是 Apple 指南所记载的只读布尔属性,用于判断网页是否正以 独立模式显示。
  • 标准的 display-mode 媒体特性用于测试 Web 应用是显示在普通浏览器标签页中,还是以独立 应用、全屏等其他方式显示。它的值标识浏览器实际采用的模式,可能与清单请求的模式不同。
// 优先使用标准媒体特性;回退到 Apple 的旧属性。
const standalone =
('standalone' in window.navigator && window.navigator.standalone === true) ||
window.matchMedia('(display-mode: standalone)').matches;
if (standalone) {
hideInstallHint(); // 正以独立模式显示——无需再教学
} else {
// iOS 上不存在安装事件,唯一的"提示"就是你自己的文案。
showShareMenuInstructions(); // "点按分享,然后添加到主屏幕"
}

请在用户访问过几次之后再显示该提示,而不是首次加载就弹出;并且绝不要在页面正以独立模式 显示时渲染它——在已启动的 Web 应用里出现安装横幅纯属噪音。

能力 iOS / iPadOS 说明
添加到主屏幕 支持,经分享菜单 Safari 自第一代 iPhone 起;第三方浏览器自 16.4 起。
beforeinstallprompt 不支持 非标准、可用性有限——没有程序化安装。
以 Web 应用方式打开 支持 16.4 上需清单 display: standalone/fullscreen;iOS 26 上每个添加的站点默认如此。
App 切换器条目 支持 Web 应用与 Safari 分开预览。
Service Worker 支持,但非必需 主屏幕 Web 应用从不要求它;原始 Web Push 需要,Declarative Web Push(18.4+)不需要。
Web Push 支持(16.4+),仅主屏幕 Web 应用 请求必须响应直接用户交互。18.4 起 Declarative Web Push 额外提供 window.pushManager。
Badging API 支持(16.4+),仅主屏幕 Web 应用 用户允许通知后,图标上才出现计数。
清单 id 支持(16.4+) 与用户所取名称组合,标识每一份安装。
清单 icons 支持(15.4+) 若同时存在,apple-touch-icon 优先。
屏幕唤醒锁 / 屏幕方向 / 用户激活 支持(16.4+) WebKit 将其列为 16.4 面向 Web 应用开发者的新能力。
默认持久存储 不支持 除非源主动申请,存储都是尽力而为。

各浏览器的分特性数据,请查看 /zh/compatibility/。

iOS 从一开始就支持同一个 Web 应用安装多次,WebKit 把它视为有意为之:多账号、工作与个人 分离等等。自 16.4 起,清单的 id 成员——一个 URL 形态的 Web 应用唯一标识符——会与用户添加 时输入的名称组合起来,使每份副本都可区分。这正是“专注模式”能只静音“Shiny(个人)“而放行 “Shiny(工作)“的原因;当用户在多台设备上给同一站点取相同名称时,专注设置也因此得以同步。

显式声明 id 依然值得做:web.dev 的论点是,显式的 id 直接定义了标识符,使其不再依赖 start_url 或清单文件所在的位置。

  • 自行提供“点按分享,然后添加到主屏幕”的说明——没有任何浏览器 UI 会替你提示。
  • 按 (display-mode: standalone) 分支(以 navigator.standalone 作为旧版回退),绝不要按 User-Agent 字符串。
  • 在清单中声明图标,同时清楚一个随手留下的 apple-touch-icon 会覆盖它们。
  • 设置清单 id,让多次安装可区分、专注设置可同步。
  • 不要依赖 apple-mobile-web-app-capable 在 iOS 26 上决定 Web 应用模式——由用户的“以 Web 应用打开”开关决定。
  • 即便主屏幕条目不要求 Service Worker,也要注册一个;原始 Web Push(16.4+)需要它。Declarative Web Push(18.4+)则通过 window.pushManager 订阅,无需 Service Worker。
  • 把已存数据当作尽力而为:它在该源未超配额、设备仍有空间、且用户没有清除数据时才会保留;而且在开启跨站跟踪保护时,若某个源在最近七天的浏览器使用中没有点击或轻点,Safari 会主动删除它由脚本创建的数据。navigator.storage.persist() 是主动申请的入口,而 Safari 会依据交互历史自动作答,不向用户显示提示。
  • 在真机上测试——上述行为都是主屏幕行为,不是标签页行为。