跳到主要内容

远程通知

备注

不同平台之间的推送有些不一样,此处以IOS为例。

备注

发送的aps如果json格式不对也会出现Response(200)终端却并没有收到推送的现象。

  • 大括号是否齐全
  • json数据字段的key是否正常被""包裹,注意python中的json5库会自动去掉key的双引号。

证书模式

较少使用,不太方便,此处暂时省略。

Token模式

能力、证书配置在最新版本的Xcode可以在应用界面中可以全自动配置,此处省略。

生成Token

官网文档

  1. 生成新的key

Apple Developer

  1. 数据准备

KEYID

Apple Developer账号信息

团队ID:

KeyDescription
alg编码算法,苹果目前只支持ES256
kid上面记录下来的Key ID
iss你的开发者账号的Team ID
iatToken的生成时间(UTC时区,从Epoch以来的秒数)

JSON示例:

{
"alg" : "ES256",
"kid" : "ABC123DEFG"
}
{
"iss": "DEF123GHIJ",
"iat": 1437179036
}
  1. 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)
  1. 注册终端并且获取deviceToken

新建一个AppDelegate文件去对应应用的生命周期

AppDelegate.swift
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()
}
}
  1. 应用申请通知权限
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 Token
  • apns-topic 应用的App Id/Bundle Id
  • deviceToken 设备的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>"