WWCD 2018:通知分组的使用

4,492 阅读7分钟

Session 711 : Using Grouped Notifications

前言

iOS 12 为通知带来了不少新特性,而在这些新特性中通知分组最为吸引眼球。对于这次通知的新特性还不了解的同学可以点击这里。 本文针对这一新特性从以下几个方面进行阐述:

  • 按应用分组和自定义通知分组( App grouping & Custom grouping )
  • 通知摘要( Notification Groups Summaries )
  • 通知分组的设计准则

1. 按应用分组和自定义分组( App grouping & Custom grouping )

按应用分组

默认情况下系统会按照应用( bundle id )把收到的通知分成不同的组,每个组的顶层是该组收到的最新通知,伴随着当前组的通知摘要( group summaries )。

我们可以通过左滑管理,查看,清除组里的通知,也可以点击展开组里的通知逐个处理。

自定义分组

按应用分组的模式无法满足丰富的显示通知场景,重要的通知可能会被不重要的通知遮盖而无法达到提示的作用,这时候就该自定义分组大显身手了。 要实现自定义分组开发者只需要给想要额外分组的通知加上线程标识( thread identifier )即可:本地通知直接为 content 的 threadIdentifier 参数赋值,而对于远程通知而言,在通知的 payload 中添加以 thread-id 为键的键值对。

// 本地通知
let content = UNMutableNotificationContent()
content.title = "Notifications Team"
content.body = "WWDC session after party"
// 自定义标识
content.threadIdentifier = "notifications-team-chat"

// 远程通知
{
	"aps" : {
		"alert" : {
			"title" : "New Photo",
			"body" : "WWDC session after party"
		}
		// 自定义标识
		"thread-id" : "notifications-team-chat"
	}
}

注:Session 711Session 710 在远程推送的 payload 中写法有出入,看官方文档的解释应该以这里为准。

2. 通知摘要( Notification Groups Summaries )

当通知被分组之后,如果没有一个提示信息告诉我们当前分组中有多少条信息,这些信息的简单描述是一件很不方便的事情,苹果提供了通知摘要为我们实现这个功能。

简单通知摘要( Simple Notification Group Summary )

在通知概要以及如何自定义通知摘要之前,有一个概念需要先拿出来说一下:

摘要参数数量( Summary Argument Count )

摘要参数数量代表了 1 条通知所包含的内容量。这样说可能有点抽象,我们以 Podcase 为例,Podcast 为了减少推送的总量,会把某个节目同一时间更新的几条内容合成一个推送,这就形成了我们下面看到的情况。

iOS 12 为 UNNotificationContent 和 UNMutableNotificationContent 增加了新的 NSUInteger 参数:summaryArgumentCount 。如果不设置,这个值默认为 1 。

// 本地推送设置
let content = UNMutableNotificationContent()
content.body = "…"
content.threadIdentifier = "…"
content.summaryArgument = "Song by Song"
content.summaryArgumentCount = 3

// 远程推送设置
{
	"aps" : {
		"alert" : {
			"body" : "…",
			"summary-arg" : "Song by Song",
			"summary-arg-count" : 3
		},
		"thread-id" : "notifications-team"
	}
}

包含所有未展示通知摘要参数数的通知摘要

了解了摘要参数数量之后,我们来看一下最基本的通知摘要组成格式。

let summaryFormat = "%u more messages"

return UNNotificationCategory(
	identifier: "category-identifier",
	actions: [],
	intentIdentifiers: [],
	hiddenPreviewsBodyPlaceholder: nil,
	categorySummaryFormat: summaryFormat,
	options: []
)

summaryFormat 中使用了 %u 来为通知组中所有未展示通知的摘要参数数量之和占位( 未展示通知指通知组中除了栈顶通知的其余通知 )。

隐藏通知详情时展示的通知摘要( Hidden Previews Summary Customization )

我们可以看到在 UNNotificationCategory 初始化时可以设置的参数中有另外一个参数 hiddenPreviewsBodyPlaceholder ,如果用户设置了在锁屏情况下不展示通知具体内容( iOS 11 新增的特性 ),我们可以设定这个参数来展示不同的摘要内容。

let summaryFormat = "%u more messages"
let hiddenPreviewsPlaceholder = "%u messages"

return UNNotificationCategory(
	identifier: "category-identifier",
	actions: [],
	intentIdentifiers: [],
	hiddenPreviewsBodyPlaceholder: hiddenPreviewsPlaceholder,
	categorySummaryFormat: summaryFormat,
	options: []
)

在 hiddenPreviewsBodyPlaceholder 也是用了 %u 来为通知组中所有通知的摘要参数数量之和占位。

为了更好的理解这两种情况的区别,我们依然以 Podcast 的那张图为例。如果我们设置了不展示通知具体内容,通知组的摘要中会显示:“ 9 new episodes are available. ” ( 2 + 3 + 1 + 3 );而如果我们没有设置,则会显示:“ 7 new episodes are available. ” ( 3 + 1 + 3 )。

带参数的通知摘要( Notification Group Summary with Arguments )

上图中展现的是 Messages 群聊时通知摘要会提示你消息的来源,要实现这个效果需要调整我们的 summaryFormat 字符串。

let summaryFormat = "%u more messages from %@"

return UNNotificationCategory(
	identifier: "group-messages",
	actions: [],
	intentIdentifiers: [],
	hiddenPreviewsBodyPlaceholder: nil,
	categorySummaryFormat: summaryFormat,
	options: []
)

我们使用 %@ 来为摘要参数占位。那什么是摘要参数?

摘要参数( Summary Argument )

和摘要参数数量一样,摘要参数是 iOS 12 UNNotificationContent 新增加的用来拼成 UNNotificationCategory 的摘要格式化字符串。

// 本地推送设置
let content = UNMutableNotificationContent()
content.body = "…"
content.threadIdentifier = "notifications-team"
content.summaryArgument = "Kritarth"

// 远程推送设置
{
	"aps" : {
		"alert" : {
			"body" : "…",
			"summary-arg" : "Kritarth"
		},
		"thread-id" : "notifications-team"
	}
}

iOS 系统会为我们将未展示的通知中的摘要参数合成一个字符串展示给用户。 注:摘要参数不需要每条通知都不同,我们可以使用相同的摘要参数,系统会筛选不同的摘要参数进行字符串合成,下图中邮件就是用 iCloud 邮件账户作为摘要参数。

通知摘要的本地化( Summary Localization )

如果我们需要让 App 应对不同语言的场景,只需要两步,简直比把大象放进冰箱还方便!

  • 将 summaryFormat 转换成本地化字符串
let summaryFormat = NSString.localizedUserNotificationString(
	forKey: "NOTIFICATION_SUMMARY",
	arguments: nil
)
  • 根据不同的语言提供不同的 .stringsdict 文件 关于 . stringsdict 文件的具体内容就不在这里赘述了,点击这里查看如何使用,点击这里查看文件格式。
    上图以英语为例,如果要支持别的语言,只需要替换高亮出的文本即可。

合并同个通知组中不同的摘要

由于通知摘要是定义在 UNNotificationCategory ,而线程标识是定义在通知内容中,有时候会碰到需要合并同个通知组中不同的摘要的情况。

  • 如果通知组中所有的通知都是不带摘要参数的简单摘要,所有的摘要会被合并。例:5 more messages and 3 more photos ;
  • 如果通知组中含有带摘要参数的摘要,合并后的通知摘要会被降级为默认摘要。例:8 more notifications ;

使用通知内容扩展通知的分组

  • 扩展默认接收栈顶的通知,并调用 didReceive 接口
public protocol UNNotificationContentExtension :  NSObjectProtocol {
	public func didReceive(_ notification: UNNotification)
}
  • 通过调用系统 API 获取分组中其余的通知
class UNUserNotificationCenter : NSObject {
func getDeliveredNotifications(completionHandler:([UNNotification]) -> Swift.Void)
}
  • 扩展页面展开时有新的同一分组的通知进来,同样会调用 didReceive 接口
public protocol UNNotificationContentExtension :  NSObjectProtocol {
	public func didReceive(_ notification: UNNotification)
}
  • 当通知被展示完毕后,你需要将通知从通知中心移除
class UNUserNotificationCenter : NSObject {
	func removeDeliveredNotifications(withIdentifiers identifiers: [String])
}

3. 通知分组的设计准则

Session 中以系统的 3 个 App 为例提出了在设计通知分类的时候需要注意的地方。

将重要的,可交互的通知从消息更新类通知中分离出来( Separate important, actionable notifications from informative updates )

日历 App 在 iOS 12 中将提醒的通知单独设置 threadIdentifier ,其余通知使用默认分组。为更重要的通知设置不同的分组能更好的起到通知的作用。

为有意义的,私人的通讯建立分组( Create groups for meaningful personal communications )

对于类似 Messages 的 App 来说,它们推送的通知对于使用者常常是有意义的,这类的信息在通知中心的停留时间相对较短( 人们倾向于尽快处理这类信息 ),因此为这些通知生成单独的分类是很有必要的。

尊重用户的优先级和组别( Respect the user’s priorities and organization )

以邮件 App 为例,邮件默认是按照不同的账户分组的( iCloud 账户,Gmail 账户等 ),如果用户将某些发件人标记为 VIP ,邮件会单独为这个用户建立一个分组。当我们为某个邮件设置了 “通知我” 时,回复了这个邮件主题的通知也会被单独归到一个分组中。

总结

通知分组强化了 App 推送的信息展示能力,对于开发者来说为通知设置线程标识也十分便捷。相信随着 iOS 12 的到来能有越来越多的 App 利用自定义通知分组的特性为用户提供更有效的通知。

查看更多 WWDC 18 相关文章请前往 老司机x知识小集xSwiftGG WWDC 18 专题目录 - 简书