iOS 中的通知主要分为 2 种,本地通知和远程通知。
UserNotifications
模块。UNMutableNotificationContent
,可以设置:
(1)title:通知标题。
(2)subtitle:通知副标题。
(3)body:通知体。
(4)sound:声音。
(5)badge:角标。
(6)userInfo:额外信息。
(7)categoryIdentifier:分类唯一标识符。
(8)attachments:附件,可以是图片、音频和视频,通过下拉通知显示。UNTimeIntervalNotificationTrigger
:一段时间后触发。
(2)UNCalendarNotificationTrigger
:指定日期时间触发。
(3)UNLocationNotificationTrigger
:根据位置触发。UNNotificationRequest
。UNNotificationRequest
添加到UNUserNotificationCenter
。import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 请求通知权限
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { // 横幅,声音,标记
(accepted, error) in
if !accepted {
print("用户不允许通知")
}
}
return true
}
import CoreLocation
import UIKit
import UserNotifications
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// 一段时间后触发
@IBAction func timeInterval(_ sender: Any) {
// 设置推送内容
let content = UNMutableNotificationContent()
content.title = "你好"
content.subtitle = "Hi"
content.body = "这是一条基于时间间隔的测试通知"
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "feiji.wav"))
content.badge = 1
content.userInfo = ["username": "YungFan", "career": "Teacher"]
content.categoryIdentifier = "testUserNotifications1"
setupAttachment(content: content)
// 设置通知触发器
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// 设置请求标识符
let requestIdentifier = "com.abc.testUserNotifications2"
// 设置一个通知请求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
// 将通知请求添加到发送中心
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
// 指定日期时间触发
@IBAction func dateInterval(_ sender: Any) {
// 设置推送内容
let content = UNMutableNotificationContent()
content.title = "你好"
content.body = "这是一条基于日期的测试通知"
// 时间
var components = DateComponents()
components.year = 2021
components.month = 5
components.day = 20
// 每周一上午8点
// var components = DateComponents()
// components.weekday = 2 // 周一
// components.hour = 8 // 上午8点
// components.minute = 30 // 30分
// 设置通知触发器
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
// 设置请求标识符
let requestIdentifier = "com.abc.testUserNotifications3"
// 设置一个通知请求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
// 将通知请求添加到发送中心
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
// 根据位置触发
@IBAction func locationInterval(_ sender: Any) {
// 设置推送内容
let content = UNMutableNotificationContent()
content.title = "你好"
content.body = "这是一条基于位置的测试通知"
// 位置
let coordinate = CLLocationCoordinate2D(latitude: 31.29065118, longitude: 118.3623587)
let region = CLCircularRegion(center: coordinate, radius: 500, identifier: "center")
region.notifyOnEntry = true // 进入此范围触发
region.notifyOnExit = false // 离开此范围不触发
// 设置触发器
let trigger = UNLocationNotificationTrigger(region: region, repeats: true)
// 设置请求标识符
let requestIdentifier = "com.abc.testUserNotifications"
// 设置一个通知请求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
// 将通知请求添加到发送中心
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
extension ViewController {
func setupAttachment(content: UNMutableNotificationContent) {
let imageURL = Bundle.main.url(forResource: "img", withExtension: ".png")!
do {
let imageAttachment = try UNNotificationAttachment(identifier: "iamgeAttachment", url: imageURL, options: nil)
content.attachments = [imageAttachment]
} catch {
print(error.localizedDescription)
}
}
}
远程通知是指在联网的情况下,由远程服务器推送给客户端的通知,又称 APNs(Apple Push Notification Services)。在联网状态下,所有设备都会与 Apple 服务器建立长连接,因此不管应用是打开还是关闭的情况,都能接收到服务器推送的远程通知。
远程通知流程.png
UIApplication.shared.registerForRemoteNotifications()
向 APNs 请求 deviceToken。func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
获取 deviceToken。如果正常获取到 deviceToken,即表示注册成功,可以进行远程通知的推送,最后需要将其发送给应用服务器。注意: UIApplication.shared.registerForRemoteNotifications()
向 APNs 请求 deviceToken。func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
获取 deviceToken。如果正常获取到 deviceToken,即表示注册成功,可以进行远程通知的推送,最后需要将其发送给应用服务器。Token Authentication 是 APNs 新推出的推送鉴权方式,它如下优势: (1)同一个开发者账号下的所有 App 无论是测试还是正式版都能使用同一个 Key 来发送而不需要为每个 App 生成证书。 (2)生成 Key 的过程相对简单,不需要繁琐的证书操作过程,并且它不再有过期时间,无需像证书那样需要定期重新生成。。
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 请求通知权限
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
accepted, _ in
if !accepted {
print("用户不允许通知。")
}
}
// 向APNs请求deviceToken
UIApplication.shared.registerForRemoteNotifications()
return true
}
// deviceToken请求成功回调
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
var deviceTokenString = String()
let bytes = [UInt8](deviceToken)
for item in bytes {
deviceTokenString += String(format: "%02x", item & 0x000000FF)
}
// 打印获取到的token字符串
print(deviceTokenString)
// 通过网络将token发送给服务端
}
// deviceToken请求失败回调
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
}
注意:远程通知不支持模拟器(直接进入deviceToken请求失败回调),必须在真机测试。
{
"aps":{
"alert":{
"title":"测试",
"subtitle":"远程推送",
"body":"这是一条从远处而来的通知"
},
"sound":"default",
"badge":1
}
}
xcrun simctl push booted developer.yf.TestUIKit /Users/yangfan/Desktop/playload.json
另一种方法是将 APNs 文件直接拖到 iOS 模拟器中。准备一个后缀名为.apns
的文件,其内容和上面的 JSON 文件差不多,但是添加了一个Simulator Target Bundle
,用于描述 App 的Bundle Identifier
。
{
"Simulator Target Bundle": "developer.yf.TestUIKit",
"aps":{
"alert":{
"title":"测试",
"subtitle":"远程推送",
"body":"这是一条从远处而来的通知"
},
"sound":"default",
"badge":1
}
}
默认情况下,App 只有在后台才能收到通知提醒,在前台无法收到通知提醒,如果前台也需要提醒可以进行如下处理。
class NotificationHandler: NSObject, UNUserNotificationCenterDelegate {
// 前台展示通知
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// 前台通知一般不设置badge
completionHandler([.list, .banner, .sound])
// 如果不想显示某个通知,可以直接用 []
// completionHandler([])
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
// 自定义通知回调类,实现通知代理
let notificationHandler = NotificationHandler()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 设置代理
UNUserNotificationCenter.current().delegate = notificationHandler
return true
}
}
// 手动添加角标
UIApplication.shared.applicationIconBadgeNumber = 10
// 清理角标
UIApplication.shared.applicationIconBadgeNumber = 0