iOS14開發(fā)- 通知

iOS 中的通知主要分為 2 種,本地通知和遠(yuǎn)程通知。

本地通知

使用步驟

  1. 導(dǎo)入UserNotifications模塊。
  2. 申請(qǐng)權(quán)限。
  3. 創(chuàng)建通知內(nèi)容UNMutableNotificationContent,可以設(shè)置:
    (1)title:通知標(biāo)題。
    (2)subtitle:通知副標(biāo)題。
    (3)body:通知體。
    (4)sound:聲音。
    (5)badge:角標(biāo)。
    (6)userInfo:額外信息。
    (7)categoryIdentifier:分類唯一標(biāo)識(shí)符。
    (8)attachments:附件,可以是圖片、音頻和視頻,通過(guò)下拉通知顯示。
  4. 指定本地通知觸發(fā)條件,有 3 種觸發(fā)方式:
    (1)UNTimeIntervalNotificationTrigger:一段時(shí)間后觸發(fā)。
    (2)UNCalendarNotificationTrigger:指定日期時(shí)間觸發(fā)。
    (3)UNLocationNotificationTrigger:根據(jù)位置觸發(fā)。
  5. 根據(jù)通知內(nèi)容和觸發(fā)條件創(chuàng)建UNNotificationRequest。
  6. UNNotificationRequest添加到UNUserNotificationCenter

案例

  • 申請(qǐng)授權(quán)(異步操作)。
import UserNotifications

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 請(qǐng)求通知權(quán)限
    UNUserNotificationCenter.current()
        .requestAuthorization(options: [.alert, .sound, .badge]) { // 橫幅,聲音,標(biāo)記
            (accepted, error) in
            if !accepted {
                print("用戶不允許通知")
            }
    }
    
    return true
}
  • 發(fā)送通知。
import CoreLocation
import UIKit
import UserNotifications

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // 一段時(shí)間后觸發(fā)
    @IBAction func timeInterval(_ sender: Any) {
        // 設(shè)置推送內(nèi)容
        let content = UNMutableNotificationContent()
        content.title = "你好"
        content.subtitle = "Hi"
        content.body = "這是一條基于時(shí)間間隔的測(cè)試通知"
        content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "feiji.wav"))
        content.badge = 1
        content.userInfo = ["username": "YungFan", "career": "Teacher"]
        content.categoryIdentifier = "testUserNotifications1"
        setupAttachment(content: content)

        // 設(shè)置通知觸發(fā)器
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
        
        // 設(shè)置請(qǐng)求標(biāo)識(shí)符
        let requestIdentifier = "com.abc.testUserNotifications2"
        // 設(shè)置一個(gè)通知請(qǐng)求
        let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
        // 將通知請(qǐng)求添加到發(fā)送中心
        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
    }

    // 指定日期時(shí)間觸發(fā)
    @IBAction func dateInterval(_ sender: Any) {
        // 設(shè)置推送內(nèi)容
        let content = UNMutableNotificationContent()
        content.title = "你好"
        content.body = "這是一條基于日期的測(cè)試通知"
        
        // 時(shí)間
        var components = DateComponents()
        components.year = 2021
        components.month = 5
        components.day = 20
        // 每周一上午8點(diǎn)
        // var components = DateComponents()
        // components.weekday = 2 // 周一
        // components.hour = 8 // 上午8點(diǎn)
        // components.minute = 30 // 30分
        // 設(shè)置通知觸發(fā)器
        let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
        
        // 設(shè)置請(qǐng)求標(biāo)識(shí)符
        let requestIdentifier = "com.abc.testUserNotifications3"
        // 設(shè)置一個(gè)通知請(qǐng)求
        let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
        // 將通知請(qǐng)求添加到發(fā)送中心
        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
    }

    // 根據(jù)位置觸發(fā)
    @IBAction func locationInterval(_ sender: Any) {
        // 設(shè)置推送內(nèi)容
        let content = UNMutableNotificationContent()
        content.title = "你好"
        content.body = "這是一條基于位置的測(cè)試通知"
        
        // 位置
        let coordinate = CLLocationCoordinate2D(latitude: 31.29065118, longitude: 118.3623587)
        let region = CLCircularRegion(center: coordinate, radius: 500, identifier: "center")
        region.notifyOnEntry = true // 進(jìn)入此范圍觸發(fā)
        region.notifyOnExit = false // 離開此范圍不觸發(fā)
        // 設(shè)置觸發(fā)器
        let trigger = UNLocationNotificationTrigger(region: region, repeats: true)
        // 設(shè)置請(qǐng)求標(biāo)識(shí)符
        let requestIdentifier = "com.abc.testUserNotifications"
        
        // 設(shè)置一個(gè)通知請(qǐng)求
        let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
        // 將通知請(qǐng)求添加到發(fā)送中心
        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)
        }
    }
}

遠(yuǎn)程通知(消息推送)

遠(yuǎn)程通知是指在聯(lián)網(wǎng)的情況下,由遠(yuǎn)程服務(wù)器推送給客戶端的通知,又稱 APNs(Apple Push Notification Services)。在聯(lián)網(wǎng)狀態(tài)下,所有設(shè)備都會(huì)與 Apple 服務(wù)器建立長(zhǎng)連接,因此不管應(yīng)用是打開還是關(guān)閉的情況,都能接收到服務(wù)器推送的遠(yuǎn)程通知。

遠(yuǎn)程通知流程.png

實(shí)現(xiàn)原理

  1. App 打開后首先發(fā)送 UDID 和 BundleID 給 APNs 注冊(cè),并返回 deviceToken(圖中步驟 1,2,3)。
  2. App 獲取 deviceToken 后,通過(guò) API 將 App 的相關(guān)信息和 deviceToken 發(fā)送給應(yīng)用服務(wù)器,服務(wù)器將其記錄下來(lái)。(圖中步驟 4)
  3. 當(dāng)要推送通知時(shí),應(yīng)用服務(wù)器按照 App 的相關(guān)信息找到存儲(chǔ)的 deviceToken,將通知和 deviceToken 發(fā)送給 APNs。(圖中步驟 5)
  4. APNs 通過(guò) deviceToken,找到指定設(shè)備的指定 App, 并將通知推送出去。(圖中步驟 6)

實(shí)現(xiàn)步驟

  1. 在開發(fā)者網(wǎng)站的 Identifiers 中添加 App IDs,并在 Capabilities 中開啟 Push Notifications
  2. Certificates 中創(chuàng)建一個(gè) Apple Push Notification service SSL (Sandbox & Production) 的 APNs 證書并關(guān)聯(lián)第一步中的 App IDs,然后將證書下載到本地安裝(安裝完可以導(dǎo)出 P12 證書)。
  3. 在項(xiàng)目中選擇 Capability,接著開啟 Push Notifications,然后在 Background Modes 中勾選 Remote notifications
  4. 申請(qǐng)權(quán)限。
  5. 通過(guò)UIApplication.shared.registerForRemoteNotifications()向 APNs 請(qǐng)求 deviceToken。
  6. 通過(guò)func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)獲取 deviceToken。如果正常獲取到 deviceToken,即表示注冊(cè)成功,可以進(jìn)行遠(yuǎn)程通知的推送,最后需要將其發(fā)送給應(yīng)用服務(wù)器。注意:
    • App 重新啟動(dòng)后,deviceToken 不會(huì)變化。
    • App 卸載后重新安裝,deviceToken 發(fā)生變化。
  7. 通知測(cè)試。

AppDelegate

import UserNotifications

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 請(qǐng)求通知權(quán)限
        UNUserNotificationCenter.current()
            .requestAuthorization(options: [.alert, .sound, .badge]) {
                accepted, _ in
                if !accepted {
                    print("用戶不允許通知。")
                }
            }
            
        // 向APNs請(qǐng)求deviceToken
        UIApplication.shared.registerForRemoteNotifications()

        return true
    }

    // deviceToken請(qǐng)求成功回調(diào)
    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)
        
        // 通過(guò)網(wǎng)絡(luò)將token發(fā)送給服務(wù)端
    }
    
    // deviceToken請(qǐng)求失敗回調(diào)
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print(error.localizedDescription)
    }
}

注意:遠(yuǎn)程通知不支持模擬器(直接進(jìn)入deviceToken請(qǐng)求失敗回調(diào)),必須在真機(jī)測(cè)試。

測(cè)試

真機(jī)測(cè)試

  1. 將 App 安裝到真機(jī)上。
  2. 通過(guò)軟件(如 APNs)或者第三方進(jìn)行測(cè)試,但都需要進(jìn)行相關(guān)內(nèi)容的設(shè)置。
    (1)證書方式需要:P12 證書 + Bundle Identifier + deviceToken。
    (2)Token 方式需要:P8 AuthKey + Team ID + Key ID + Bundle Identifier + deviceToken。

模擬器測(cè)試—使用JSON文件

  • JSON文件。
{
  "aps":{
    "alert":{
      "title":"測(cè)試",
      "subtitle":"遠(yuǎn)程推送",
      "body":"這是一條從遠(yuǎn)處而來(lái)的通知"
    },
    "sound":"default",
    "badge":1
  }
}
  • 命令。
xcrun simctl push booted developer.yf.TestUIKit /Users/yangfan/Desktop/playload.json

模擬器測(cè)試—使用APNS文件

另一種方法是將 APNs 文件直接拖到 iOS 模擬器中。準(zhǔn)備一個(gè)后綴名為.apns的文件,其內(nèi)容和上面的 JSON 文件差不多,但是添加了一個(gè)Simulator Target Bundle,用于描述 App 的Bundle Identifier。

  • APNs文件。
{
  "Simulator Target Bundle": "developer.yf.TestUIKit",
  "aps":{
    "alert":{
      "title":"測(cè)試",
      "subtitle":"遠(yuǎn)程推送",
      "body":"這是一條從遠(yuǎn)處而來(lái)的通知"
    },
    "sound":"default",
    "badge":1
  }
}

前臺(tái)處理

默認(rèn)情況下,App 只有在后臺(tái)才能收到通知提醒,在前臺(tái)無(wú)法收到通知提醒,如果前臺(tái)也需要提醒可以進(jìn)行如下處理。

  • 創(chuàng)建 UNUserNotificationCenterDelegate。
class NotificationHandler: NSObject, UNUserNotificationCenterDelegate {
    // 前臺(tái)展示通知
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // 前臺(tái)通知一般不設(shè)置badge
        completionHandler([.list, .banner, .sound])

        // 如果不想顯示某個(gè)通知,可以直接用 []
        // completionHandler([])
    }
}
  • 設(shè)置代理。
class AppDelegate: UIResponder, UIApplicationDelegate {
    // 自定義通知回調(diào)類,實(shí)現(xiàn)通知代理
    let notificationHandler = NotificationHandler()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 設(shè)置代理
        UNUserNotificationCenter.current().delegate = notificationHandler
        
        return true
    }
}

角標(biāo)設(shè)置

  • 不論是本地還是遠(yuǎn)程通知,前臺(tái)通知一般不會(huì)設(shè)置角標(biāo)提醒,所以只需要針對(duì)后臺(tái)通知處理角標(biāo)即可。
  • 通知的角標(biāo)不需要手動(dòng)設(shè)置,會(huì)自動(dòng)根據(jù)通知進(jìn)行設(shè)置。

設(shè)置

// 手動(dòng)添加角標(biāo)
UIApplication.shared.applicationIconBadgeNumber = 10

// 清理角標(biāo)
UIApplication.shared.applicationIconBadgeNumber = 0
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容