远程通知
不同平台之间的推送有些不一样,此处以IOS为例。
发送的aps如果json格式不对也会出现Response(200)终端却并没有收到推送的现象。
- 大括号是否齐全
- json数据字段的key是否正常被
""
包裹,注意python中的json5库会自动去掉key的双引号。
证书模式
较少使用,不太方便,此处暂时省略。
Token模式
能力、证书配置在最新版本的Xcode可以在应用界面中可以全自动配置,此处省略。
生成Token
- 生成新的key
- 数据准备
KEYID
团队ID:
Key | Description |
---|---|
alg | 编码算法,苹果目前只支持ES256 |
kid | 上面记录下来的Key ID |
iss | 你的开发者账号的Team ID |
iat | Token的生成时间(UTC时区,从Epoch以来的秒数) |
JSON示例:
{
"alg" : "ES256",
"kid" : "ABC123DEFG"
}
{
"iss": "DEF123GHIJ",
"iat": 1437179036
}
- JWT编码
这个生成的就是最终向APNS服务器发送的请求的请求头中的authorization: bearer ***
中的***
字段。
python版本
import jwt
import time
teamId = '***'
keyId = '***'
payload = {
"iss": teamId,
"iat": time.time()
}
headers = {
"alg" : "ES256",
"kid" : keyId
}
# Apple Developer下载的p8文件
with open('./AuthKey_****.p8', 'r') as f:
privateKey = f.read()
token = jwt.encode(payload=payload, algorithm='ES256', headers=headers, key=privateKey)
print('token:', token)
- 注册终端并且获取deviceToken
新建一个AppDelegate文件去对应应用的生命周期
import SwiftUI
// 扩展Data,将Data编码转成正常字符串
extension Data {
var hexString: String {
return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.you’re
// 申请注册本机
UIApplication.shared.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken
deviceToken: Data) {
// 如果注册成功,那么会调用此处函数
print("deviceToken:", deviceToken.hexString)
// self.sendDeviceTokenToServer(data: deviceToken)
}
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError
error: Error) {
print("error:", error)
// Try again later.
}
}
didRegisterForRemoteNotificationsWithDeviceToken
返回的deviceToken默认已经变成Data格式,要转成字符串需要扩展Data类型,或者直接处理数据也行。
// 扩展Data,将Data编码转成正常字符串
extension Data {
var hexString: String {
return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
}
}
- 应用申请通知权限
func requestNotificationAuth(){
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
print("申请权限成功。")
} else {
print("D'oh!")
}
}
}
如果没有调用didFinishLaunchingWithOptions
,可能是你没有授权应用,去设置,通知里面检查一下权限状态。
测试
Development server: api.sandbox.push.apple.com:443
Production server: api.push.apple.com:443`
curl -XPOST https://api.sandbox.push.apple.com/3/device/<deviceToken> -H "authorization: bearer <token>" -H "apns-topic: <bundleId>" -H "apns-id: <UUID>" -d "<data>"
apns-id
你的这条推送的ID,如果推送出现了问题,那么这部分ID会返回给你的服务端。authorization
JWT Tokenapns-topic
应用的App Id/Bundle IddeviceToken
设备的ID,通过应用registerForRemoteNotifications
注册获取data
具体的通知内容
data范例
{
"aps" : {
// 弹窗信息
"alert" : {
"title" : "Game Request",
"subtitle" : "Five Card Draw",
"body" : "Bob wants to play poker"
},
// 应用图标上显示的小红点数目
"badge": 0,
// 声音简单版本 "sound": "default",
// 声音自定义版本
"sound": {
// The critical alert flag. Set to 1 to enable the critical alert.
"critical": 1,
// The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Specify the string “default” to play the system sound.
"name": "default",
// The volume for the critical alert’s sound. Set this to a value between 0 (silent) and 1 (full volume).
"volume": 1
}
},
// 自定义键值对
"gameID" : "12345678"
}
除了Apple定义的键值对以外, 你也可以往data中增加自定义的键值对(少量数据)来传递给你的App。值必须是原始类型, 像dictionary, array, string, number, 或者Boolean. 自定义键值对能够在递送给App的UNNotificationContent的userInfo的字典对象中获取到。
data参数
详细见Apple Developer generating_a_remote_notification
如果发送成功了,那么会返回一个200的Response,如果失败了,那么返回的会是{"reason": ""}
并且说明原因。
注意,如果发送的时候你本身就在APP打开的界面,那么是不会收到通知的。
静默推送(Background Notification)
静默推送时aps的内容只能包含"content-available" : 1
,但同时可以包含自定义的键值。
静默推送时通知的请求必须包含apns-push-type
请求头background
,和apns-priority
的请求头值等于5
。
{
"aps" : {
"content-available" : 1
},
"acme1" : "bar",
"acme2" : 42
}
注意点
- When the system receives a new background notification, it discards the older notification and only holds the newest one.
- If something force quits or kills the app, the system discards the held notification.
- If the user launches the app, the system immediately delivers the held notification.
接收推送
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
){
print("userInfo:", userInfo)
}
你的App只有30s来进行任何的tasks并且调用提供的completion handler
。
curl -XPOST https://api.sandbox.push.apple.com/3/device/<deviceToken> -H "apns-push-type: background" -H "apns-priority: 5" -H "authorization: bearer <token>" -H "apns-topic: <bundleId>" -H "apns-id: <UUID>" -d "<data>"