Devlog#03|闪亮亮通知

发布于:01/06/2025 16:45:04

Video: https://youtu.be/O_yyuO6ZBDY

如果你用过 Discord, Slack (Solian 也有) 之类的 App 可能会发现一些很神奇的东西。 在他们的消息通知上会有一个在神奇的头像,这是怎么做的呢?

Introducing... 在 WWDC21 推出的 Communication Notification(我译为 通信消息)! 顾名思义,就是专门给聊天软件做的通知类型。

这些都是要通过 Notification Service Extension aka. NSE 实现的。 除此之外 NSE 还能做到很多奇奇怪怪的功能,例如在通知上面放附件,修改通知信息,给通知添加操作等等。

How does it works?

又到了经典的 How does it works 环节。在这里,我们要引入三个东西, 分别是 Server-side 自己的服务器侧, APNs 苹果推送服务器, 和 Client-side 本地软件 NSE。

Server-side

服务器要干的事情很简单,就是把通知推给苹果的 APNs 推送服务。 这里的通知只能由 APNs 推送到你的手机才会出发 NSE 来修改通知内容,所以推送通知是必须的。 因此,你需要加入 Apple Developer Program aka. 付费开发者帐户。 但是写 iOS 的人大抵都应该付了这个钱吧,必要的开支啊~

推送服务器可用的很多,例如 github.com/appleboy/gorush 就是一个现成的推送服务。 或者说你可以像我一样自己用 github.com/sideshow/apns2 写一个。 亦或是你是有钱人,可以用第三方的推送服务,然后直接调他们的 API 就行

我至今还是不理解为什么推送通知要收费

如果你要学我自己写的话,我这里有代码给你参考参考,没有很多复杂的, 就是单纯的推送通知,但是因为我们的依赖关系,所以我不觉得你可以直接跑起来,但是作为参考也行。 github.com/Solsynth/HyperNet.Pusher

不管你要自己部署,用第三方的,还是自己写,都需要与 APNs 通信, 这时候你就需要密钥,当然你可以用 old fashion way 什么 .p12 和 .pem 两文件。 但是我们是现代人,有 JWT,在 Apple 这里对应的就是 .p8 密钥,那么 .p8 文件怎么获取呢?

  1. 前往 developer.apple.com/account/resources/authkeys/list 创建一个密钥,记得要钩上「Apple Push Notifications service (APNs)」这个选项
  2. 创建完成之后记得下载,一个密钥只能下载一遍,这就是你的 .p8 密钥了
  3. 记得保密好

APNs

APNs stands for Apple Notification Service. 苹果推送通知服务。 虽说这是苹果的东西你不用配置什么,但是还是有一个注意事项我想要提一下。

APNs 中有一个环境的概念,有两个环境可以用,Production(生产)和 Development(开发)。 对应环境的 Device Token 只能用于对应环境的推送,不然会收不到。 而且在 App 在 Foreground 也就是前台时,通知可能不会显示出来。记得划到主界面就能看到了。

Client-side

上文有提到一个 Device Token 的概念,它的用途是用来表示这个设备,让 APNs 能够找到这个设备, 并且把通知推送给指定设备。Device Token 需要在客户端侧获取,然后发送给服务器记住,这样服务器就可以 成功推送通知了。

Device Token 虽叫做 Token(令牌),但是我觉得这个令牌没有什么可以保密的,因为发送通知还需要你的 .p8 密钥,而且 Device Token 都是不透明的令牌,不能代表什么,所以我认为没什么问题

Device Token 这个概念不是苹果的专利,Google 的 Firebase Cloud Messaging (World-wide Android Push Notification Service) 也有这个概念,但是两家的推送令牌长得很不一样,因为本篇主要讲 iOS 侧的,Android 我也设计不深,未来找机会再讲。

至此,推送通知的 Setup 基本上完成了,现在只用创建一个 NSE 来修改通知内容即可。


现在,打开你的 XCode,找到 File > New > Target... 来创建一个 Notification Service Extension(注意:不是 Notification Content Extension

XCode 生成的文件主要有一个 didReceive 方法,它会在设备收到你这个 App 的通知的时候来调用, 修改内容,然后用这个方法的一个参数 contentHandler 方法来更新显示的通知。

注意:你需要在服务器推送通知给 APNs 时标注 mutable-content 设置为 1 来标志这个通知可以被 NSE 修改,这样它才会交给 NSE

关于如何修改通知,我这里不赘述,可以移步至 Apple 官方开发者文档

小小开发者就不在这里班门弄斧了,还是看文档妥当,不过你还是想看,我这里有我自己的实现供你参考:https://github.com/Solsynth/HyperNet.Surface/blob/master/ios/SolarNotifyService/NotificationService.swift

对了,之前写文的时候忘了一点,记得在你的 NSE 的 Info.plist 中加上这个

<key>NSUserActivityTypes</key>
<array>
		<string>INSendMessageIntent</string>
</array>

这样才能用 Communication Notification Message Intent。 如果有需要,还可以再加一个 <string>INStartCallIntent</string>

Testing

NSE 真的不好测试,所有的 Extension 都不好测试,你不知道它们什么时候会被唤起

关于测试 NSE,你首先要获取到一个 Development 环境的 Device Token(因为你在开发模式里只能获取到 Development 环境的 Token) 然后转到 icloud.developer.apple.com/dashboard/notifications which is Push Notification 的控制台,可以来发测试的通知

在发通知之前,打开你的 XCode,运行主 App,找到 Debug > Attach to Process by PID or Name... 这个选项。 不要相信 XCode 给你自动填充的你的 NSE 的 Identifier(包名),实测抓不到的。这里只用填 NSE 的 Product name 即可。 注:不要勾选 Enable Logging,NSE 会崩溃

然后去控制台推送一个 Development 环境的通知。记得打 Breakpoint 和标注 mutable-content

这下,你应该能成功 Debug 到 NSE 了

Ending

这就是我做 iOS 推送通知和 NSE 的一些心得和经验了,希望能够帮到你。最后,感谢你读完这个长文!