(7)categoryIdentifier:分类唯一标识符。 (8)attachments:附件,可以是图片、音频和视频,通过下拉通知显示。
  • 指定本地通知触发条件,有 3 种触发方式: (1) 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")!
                let imageAttachment = try UNNotificationAttachment(identifier: "iamgeAttachment", url: imageURL, options: nil)
                content.attachments = [imageAttachment]
            } catch {
                print(error.localizedDescription)
    

    远程通知(消息推送)

    远程通知是指在联网的情况下,由远程服务器推送给客户端的通知,又称 APNs(Apple Push Notification Services)。在联网状态下,所有设备都会与 Apple 服务器建立长连接,因此不管应用是打开还是关闭的情况,都能接收到服务器推送的远程通知。

  • App 打开后首先发送 UDID 和 BundleID 给 APNs 注册,并返回 deviceToken(图中步骤 1,2,3)。
  • App 获取 deviceToken 后,通过 API 将 App 的相关信息和 deviceToken 发送给应用服务器,服务器将其记录下来。(图中步骤 4)
  • 当要推送通知时,应用服务器按照 App 的相关信息找到存储的 deviceToken,将通知和 deviceToken 发送给 APNs。(图中步骤 5)
  • APNs 通过 deviceToken,找到指定设备的指定 App, 并将通知推送出去。(图中步骤 6)
  • 在开发者网站的 Identifiers 中添加 App IDs,并在 Capabilities 中开启 Push Notifications
  • Certificates 中创建一个 Apple Push Notification service SSL (Sandbox & Production) 的 APNs 证书并关联第一步中的 App IDs,然后将证书下载到本地安装(安装完可以导出 P12 证书)。
  • 在项目中选择 Capability,接着开启 Push Notifications,然后在 Background Modes 中勾选 Remote notifications
  • 申请权限。
  • 通过UIApplication.shared.registerForRemoteNotifications()向 APNs 请求 deviceToken。
  • 通过func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)获取 deviceToken。如果正常获取到 deviceToken,即表示注册成功,可以进行远程通知的推送,最后需要将其发送给应用服务器。注意:
  • App 重新启动后,deviceToken 不会变化。
  • App 卸载后重新安装,deviceToken 发生变化。
  • 通知测试。
  • AppDelegate

    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请求失败回调),必须在真机测试。

  • 将 App 安装到真机上。
  • 通过软件(如 APNs)或者第三方进行测试,但都需要进行相关内容的设置。 (1)证书方式需要:P12 证书 + Bundle Identifier + deviceToken。 (2)Token 方式需要:P8 AuthKey + Team ID + Key ID + Bundle Identifier + deviceToken
  • 模拟器测试—使用JSON文件

  • JSON文件。
  • "aps":{ "alert":{ "title":"测试", "subtitle":"远程推送", "body":"这是一条从远处而来的通知" "sound":"default", "badge":1
    xcrun simctl push booted developer.yf.TestUIKit /Users/yangfan/Desktop/playload.json
    

    模拟器测试—使用APNS文件

    另一种方法是将 APNs 文件直接拖到 iOS 模拟器中。准备一个后缀名为.apns的文件,其内容和上面的 JSON 文件差不多,但是添加了一个Simulator Target Bundle,用于描述 App 的Bundle Identifier

  • APNs文件。
  • "Simulator Target Bundle": "developer.yf.TestUIKit", "aps":{ "alert":{ "title":"测试", "subtitle":"远程推送", "body":"这是一条从远处而来的通知" "sound":"default", "badge":1

    默认情况下,App 只有在后台才能收到通知提醒,在前台无法收到通知提醒,如果前台也需要提醒可以进行如下处理。

  • 创建 UNUserNotificationCenterDelegate。
  • 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
    复制代码
  • 私信