iOS
中的推送分为本地推送和远程推送,本文主要介绍远程推送的原理。
原理
还是先从下面这张经典的图说起:
要将指定消息推送到指定的设备上,首先明确谁来发送这个消息,在
iOS
的推送中,最后实际发送消息的就是APNS
-苹果的推送服务器,APNS
只是帮忙发送消息,具体发的消息内容以及发到哪些设备上这些都需要额外的服务器告诉APNS
,也就是需要部署自己的推送服务器和APNS
对接,实际开发中经常使用第三方平台的推送服务诸如友盟,极光和个推等;其次需要确定的是APNS
如何定位到指定的设备(即只给指定的设备推送消息),而且推送的消息点击后打开指定的App
(我们自己开发的)?这里就需要Device Token
这个身份证,有了这个身份证APNS
就能准确的将消息推送到指定的设备上。
- 首先在项目中书写代码请求注册推送,这一步会弹窗告知用户让用户授权。
- 用户授权成功后,系统会向
APNS
服务器请求Device Token
。 -
App
在拿到这个Device Token
后,要将这个标识自己身份的Device Token
上送到第三方服务器集中管理。 - 当有消息需要推送时,利用第三方服务器的推送窗口,将消息以及需要发送的
Device Token
信息一并发送给APNS
,最后APNS
会将消息推送到指定的设备上。
项目配置推送环境
在Signing & Capabilities
点击Capability button
添加Push Notifications
,需要开发证书里包含了推送功能。
请求用户授权
一般在application(_:didFinishLaunchingWithOptions:)
中注册推送通知,需要在主线程中执行。
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
guard granted else { return }
// 需要检查设置中的权限,因为用户随时可能关闭权限
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
.options参数:
.badge:在图标右上角显示数字
.sound: 播放声音
.alert: 弹窗显示文本通知
.carPlay: 在CarPlay上显示通知
.provisional: 没有提醒的通知。不会有弹窗授权,但你的通知只会默默地显示在通知中心
.providesAppNotificationSettings: 表示该应用有自己的通知设置的用户界面
.criticalAlert: 忽略静音开关和请勿打扰。你需要从苹果那里获得一个特殊的权利来使用这个选项,因为它只用于非常特殊的使用情况。
处理APNS回调
在向APNS
注册推送成功后,会返回当前设备上此APP
对应的Device Token
。
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
}
在APNS
推送消息时,用户点击消息后在以下代理方法中进行处理:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
// 处理推送消息
completionHandler()
}
}
使用xcode在模拟器上模拟通知
由于消息需要通过第三方服务器转发给APNS
服务器,这里采用Xcode
模拟发送通知,首先查看模拟器的device_identifier
,点击Windows ▸ Devices and Simulators
切换到Simulators
选择当前的模拟器拷贝Identifier
。
创建一个名为
first.apn
的文件,内容如下:
{
"aps": {
"alert": "Breaking News!",
"sound": "default",
"link_url": "https://www.baidu.com"
}
}
cd
到first.apn
文件的目录下,输入指令xcrun simctl push device_identifier bundle_identifier first.apn
,其中device_identifier
和bundle_identifier
替换为刚刚获取的模拟器的device_identifier
和项目的Bundle_identifier
,例如如下所示:
这里
Xcode
利用模拟器的device_identifie
结合Bundle_identifier
来确定给哪个模拟器的哪个App
发送消息,相当于上面提到的Device Token
。
模拟真机上推送
在真机上推送消息时,第三方服务器是需要苹果的推送证书和
APNS
服务器交互的,推送证书分为开发者推送证书和生产推送证书,同时有p12
和p8
二种格式的,具体证书的申请流程不在此赘述。
在App store
中下载APNS Tool
软件,软件打开如下:
选择
Credentials
,点击Request authotization
申请授权,此时下面的CREDENTIALS
会显示相关信息包括Device token
等,拷贝下面的P8 key
字符串,这其实就是p8
证书的内容,利用拷贝的内容建立一个名为push.p8
的推送证书文件(新建纯文本文件粘贴后修改文件后缀即可)。最后将Credentials
页面的内容全部复制粘贴到Push
页面上,顺便将APNs Server
切换至Production
,点击右上角的Send push
即可收到推送。
要想在另外一台手机上收到推送,只需要输入另外一台手机的
Device Token
即可;同时测试推送的软件也可以使用Mac
端PushNotifications软件。
第三方平台个推的实现
本文选取第三方个推作为介绍,其他平台诸如友盟,极光推送等使用方式大同小异,官方Demo地址和官方视频讲解地址。
SDK的集成
在集成SDK前需要去个推官网创建应用并上传证书拿到AppId
,AppKey
和AppSecret
,直接下载官方SDK
集成即可。
通过SDK注册推送
集成了SDK
后一切操作都变得十分简单,首先是启动SDK
,填入上面所说的AppId
,AppSecret
和AppKey
:
// [ GTSDK ]:使用APPID/APPKEY/APPSECRENT启动个推
GeTuiSdk.start(withAppId: kGtAppId, appKey: kGtAppKey, appSecret: kGtAppSecret, delegate: self)
通过SDK
注册远程推送通知,使用SDK
后无需关注DeviceToken
相关逻辑(SDK
都帮忙做了),只需要关系消息回调即可。
GeTuiSdk.registerRemoteNotification([.alert, .badge, .sound])
个推消息回调
通过回调拿到个推消息。
func geTuiSdkDidReceiveNotification(_ userInfo: [AnyHashable : Any], notificationCenter center: UNUserNotificationCenter?, response: UNNotificationResponse?, fetchCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
let msg = "[ TestDemo ] \(#function) \(userInfo)"
homePage.logMsg(msg)
// [ 参考代码,开发者注意根据实际需求自行修改 ]
completionHandler?(.noData)
}
// 透传消息回调
func geTuiSdkDidReceiveSlience(_ userInfo: [AnyHashable : Any], fromGetui: Bool, offLine: Bool, appId: String?, taskId: String?, msgId: String?, fetchCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
let msg = "[ TestDemo ] \(#function) fromGetui:\(fromGetui ? "个推消息" : "APNs消息") appId:\(appId ?? "") offLine:\(offLine ? "离线" : "在线") taskId:\(taskId ?? "") msgId:\(msgId ?? "") userInfo:\(userInfo)"
homePage.logMsg(msg)
}
在个推开发者平台上推送消息
在个推官网创建一个透传消息,填写通知内容,确认后发送:
总结
本文阐述了
iOS
推送的实现原理,其中如何获取DeviceToken
最为重要,并通过相关推送App
等辅助工具进行了推送的模拟,并介绍了第三方推送的使用(其他平台的使用大同小异),弄清了推送的原理后能更好的理解iOS
的推送机制,通过APNS
服务器推送保证了消息传递的准时性和统一性。