arrow_back返回文章列表

iOS 15 通知体验:中断级别、通知摘要与用户控制

iOS 通知的难点从来不是把一条消息送到手机,而是决定它是否值得打断用户。2021 年 iOS 15 引入通知摘要和中断级别后,应用不再能简单地把“紧急”写进标题就获得即时展示。系统会综合应用声明、用户授权、专注模式和设备状态做最终决定。

这对工程和产品都是一个提醒:通知不是后台任务的日志出口,而是用户注意力的请求。开发者能表达业务紧急程度,却不能替用户决定所有通知都必须现在出现。好的实现应该让系统知道消息的性质,也让用户有清楚的关闭和调整路径。

一、先区分通知的业务价值

可以把通知分成三种:必须尽快处理的状态变化、稍后查看也不影响任务的更新、以及只在用户主动打开应用时才有价值的信息。第一类可能是一次性验证码、订单状态或安全提醒;第二类可以进入通知列表或摘要;第三类不一定值得发通知。

分类时不要从“我们希望用户看到”出发,而要从“延迟会造成什么后果”出发。营销活动、普通推荐和内容更新通常不应伪装成紧急消息。若每条消息都提高优先级,用户会关闭权限,真正重要的提醒反而失去入口。

二、iOS 15 的中断级别表达

UNNotificationInterruptionLevel 用来表达通知的重要程度和交付时机。passive 适合不点亮屏幕的低干扰更新,active 是普通即时通知,timeSensitive 用于确实需要突破部分系统控制的事项,critical 则受到更严格的能力和审核约束。选择枚举并不等于绕过用户设置,系统仍会结合授权和设备状态做判断。

下面是 Swift 片段,展示如何在 iOS 15 及以上设置普通内容和相关度。它只负责描述通知,不负责保证通知一定弹出;relevanceScore 也只是帮助系统判断摘要中的相对重要性。

let content = UNMutableNotificationContent()
content.title = "演示订单"
content.body = "状态已经更新,可在方便时查看。"
if #available(iOS 15.0, *) {
    content.interruptionLevel = .active
    content.relevanceScore = 0.4
}

let request = UNNotificationRequest(
    identifier: "order-demo",
    content: content,
    trigger: nil
)
UNUserNotificationCenter.current().add(request) { error in
    // 记录调度失败;不要在这里假设用户已经看到通知。
}

真实项目应根据事件分类设置值,不要把所有请求统一改成 timeSensitive。如果目标系统低于 iOS 15,应使用 #available 保护新属性,并让通知在旧系统上仍保持可理解的标题、正文和点击路径。

三、通知摘要改变了“送达即展示”的假设

通知摘要由用户在系统设置中安排时间和范围。应用可以通过内容的中断级别和相关度提供线索,但不能指定自己一定排在摘要第一,也不能知道用户什么时候阅读了摘要。后端因此不能把“推送已发送”当作“用户已看到”。重要任务必须在应用内保留状态,用户稍后打开时仍能看到未处理内容。

摘要也影响通知正文的写法。标题应先说明事件,正文补充最小上下文,不要把只有在应用内才能理解的内部编号放在最前面。用户可能在摘要里一次看到多条消息,稳定的业务名称、时间和下一步操作比营销口号更有用。

四、授权和系统控制要放进产品路径

请求通知权限的时机决定用户是否理解它。首次启动就弹窗,用户往往还不知道应用能提供什么价值;在用户完成一个需要提醒的动作后请求,解释成本更低。授权被拒绝后,应用应在关键页面提供可恢复入口,但不能每次进入页面都重复弹窗。

专注模式、通知摘要、静音开关和用户自定义通知设置都属于系统控制。应用可以提供“去设置查看”的入口,却不能读取用户所有设置后再强迫用户打开通知。若通知被延迟或静音,页面内应有同步状态和未读列表,避免用户只能依赖系统横幅完成任务。

五、服务端与客户端要共同维护幂等性

网络重试、设备离线和多设备登录会造成重复通知。服务端应为同一业务事件生成稳定标识,客户端在本地处理已读和去重,点击通知时根据事件 ID 跳转到当前状态,而不是直接相信推送正文。推送可能迟到、重复或顺序颠倒,业务页面必须重新拉取可信状态。

通知撤回也要谨慎。删除一个通知只能改变通知中心的展示,不会自动撤销已经执行的业务动作。对于订单、支付和安全类事件,通知只是状态的一个投影,服务端状态和应用内详情页才是事实来源。

六、边界陷阱与检查清单

  • 不把营销、普通更新和验证码都标为高优先级。
  • 不把“已提交推送”写成“用户已读”或“用户已处理”。
  • 新属性使用 #available,旧系统仍有可用正文和点击路径。
  • 权限拒绝、摘要延迟、静音和专注模式都要有应用内降级路径。
  • 通知点击按稳定事件 ID 恢复页面,不直接使用过期正文覆盖当前状态。
  • 测试重复推送、乱序推送、离线恢复、多设备登录和过期链接。

iOS 15 的通知能力让应用更容易表达“这条消息有多重要”,也更明确地把最终控制权交还给用户和系统。工程实现的目标不是争取每次都打断,而是在不能即时展示时仍然保持状态可见、任务可恢复、解释可理解。

七、前台、后台和点击路径要分别验证

同一条通知在应用前台、后台、被系统挂起和被用户点击后的行为并不相同。前台通常需要由 UNUserNotificationCenterDelegate 决定是否展示或更新页面;后台收到的远程通知也可能受到系统资源和权限影响。点击通知时,应用可能冷启动,也可能从已有页面恢复,路由代码不能只依赖内存中的列表。

测试时准备一组固定事件:普通更新、需要尽快处理的状态变化、重复事件和已经过期的事件。分别验证授权允许、授权拒绝、摘要延迟、通知被点击和应用进程不存在的情况。验收目标不是每种状态都显示横幅,而是用户最终能找到正确详情,并且不会因为过期正文误操作。

八、服务端文案也属于通知设计

通知标题和正文由客户端展示,但事件命名、去重 ID、过期时间和深链参数通常来自服务端。两端应共同约定字段含义:事件发生时间和发送时间分开;状态更新可以被覆盖;安全提醒不能使用会失效的短链接。若服务端只提供一段不可解析的营销文本,客户端就无法可靠地恢复任务。

这也是为什么通知系统需要产品、客户端和服务端一起评审。中断级别只是最后一层表达,真正决定用户信任的是消息是否准确、是否重复、点进去是否仍然有效,以及关闭通知后是否还有不打扰用户的替代入口。

上线前可以做一次“用户控制权”走查:逐项确认用户能否关闭某类通知、能否在应用内找到未读状态、能否从通知跳回正确页面,也确认应用不会把系统拒绝或摘要延迟记录成业务失败。把这些结果写入验收表,比单纯截图证明出现过横幅更接近真实体验。

参考资料

flare

保持好奇,继续思考。