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、过期时间和深链参数通常来自服务端。两端应共同约定字段含义:事件发生时间和发送时间分开;状态更新可以被覆盖;安全提醒不能使用会失效的短链接。若服务端只提供一段不可解析的营销文本,客户端就无法可靠地恢复任务。
这也是为什么通知系统需要产品、客户端和服务端一起评审。中断级别只是最后一层表达,真正决定用户信任的是消息是否准确、是否重复、点进去是否仍然有效,以及关闭通知后是否还有不打扰用户的替代入口。
上线前可以做一次“用户控制权”走查:逐项确认用户能否关闭某类通知、能否在应用内找到未读状态、能否从通知跳回正确页面,也确认应用不会把系统拒绝或摘要延迟记录成业务失败。把这些结果写入验收表,比单纯截图证明出现过横幅更接近真实体验。
参考资料
- Apple:UNNotificationInterruptionLevel,iOS 15 通知中断级别。
- Apple:UNMutableNotificationContent,通知内容配置。
- Apple:UserNotifications Framework,用户通知授权和交付机制。
- Apple:Local and Remote Notification Programming Guide,历史通知编程指南。