iOS14開發- 通知

iOS 中的通知主要分爲 2 種,本地通知和遠程通知。git

本地通知

使用步驟

  1. 導入UserNotifications模塊。
  2. 申請權限。
  3. 建立通知內容UNMutableNotificationContent,能夠設置:

(1)title:通知標題。 (2)subtitle:通知副標題。 (3)body:通知體。 (4)sound:聲音。 (5)badge:角標。 (6)userInfo:額外信息。 (7)categoryIdentifier:分類惟一標識符。 (8)attachments:附件,能夠是圖片、音頻和視頻,經過下拉通知顯示。 3. 指定本地通知觸發條件,有 3 種觸發方式: (1)UNTimeIntervalNotificationTrigger:一段時間後觸發。 (2)UNCalendarNotificationTrigger:指定日期時間觸發。 (3)UNLocationNotificationTrigger:根據位置觸發。 4. 根據通知內容和觸發條件建立UNNotificationRequest。 5. 將UNNotificationRequest添加到UNUserNotificationCenterjson

案例

  • 申請受權(異步操做)。
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 服務器創建長鏈接,所以無論應用是打開仍是關閉的狀況,都能接收到服務器推送的遠程通知。swift

遠程通知流程.png

實現原理

  1. App 打開後首先發送 UDID 和 BundleID 給 APNs 註冊,並返回 deviceToken(圖中步驟 1,2,3)。
  2. App 獲取 deviceToken 後,經過 API 將 App 的相關信息和 deviceToken 發送給應用服務器,服務器將其記錄下來。(圖中步驟 4)
  3. 當要推送通知時,應用服務器按照 App 的相關信息找到存儲的 deviceToken,將通知和 deviceToken 發送給 APNs。(圖中步驟 5)
  4. APNs 經過 deviceToken,找到指定設備的指定 App, 並將通知推送出去。(圖中步驟 6)

實現步驟

證書方式

  1. 在開發者網站的 Identifiers 中添加 App IDs,並在 Capabilities 中開啓 Push Notifications
  2. Certificates 中建立一個 Apple Push Notification service SSL (Sandbox & Production) 的 APNs 證書並關聯第一步中的 App IDs,而後將證書下載到本地安裝(安裝完能夠導出 P12 證書)。
  3. 在項目中選擇 Capability,接着開啓 Push Notifications,而後在 Background Modes 中勾選 Remote notifications
  4. 申請權限。
  5. 經過UIApplication.shared.registerForRemoteNotifications()向 APNs 請求 deviceToken。
  6. 經過func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)獲取 deviceToken。若是正常獲取到 deviceToken,即表示註冊成功,能夠進行遠程通知的推送,最後須要將其發送給應用服務器。注意:
    • App 從新啓動後,deviceToken 不會變化。
    • App 卸載後從新安裝,deviceToken 發生變化。
  7. 通知測試。

Token方式

  1. 在開發者網站的 Membership 中找到 Team ID 並記錄。
  2. Certificates, Identifiers & ProfilesKeys 中註冊一個 Key 並勾選 Apple Push Notifications service (APNs) ,最後將生成的 Key ID 記錄並將 P8 的 AuthKey 下載到本地(只能下載一次)。
  3. 在項目中選擇 Capability,接着開啓 Push Notifications,而後在 Background Modes 中勾選 Remote notifications
  4. 申請權限。
  5. 經過UIApplication.shared.registerForRemoteNotifications()向 APNs 請求 deviceToken。
  6. 經過func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)獲取 deviceToken。若是正常獲取到 deviceToken,即表示註冊成功,能夠進行遠程通知的推送,最後須要將其發送給應用服務器。
  7. 通知測試。

Token Authentication 是 APNs 新推出的推送鑑權方式,它以下優點:api

(1)同一個開發者帳號下的全部 App 不管是測試仍是正式版都能使用同一個 Key 來發送而不須要爲每一個 App 生成證書。 (2)生成 Key 的過程相對簡單,不須要繁瑣的證書操做過程,而且它再也不有過時時間,無需像證書那樣須要按期從新生成。。服務器

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請求失敗回調),必須在真機測試。markdown

測試

真機測試

  1. 將 App 安裝到真機上。
  2. 經過軟件(如 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 Identifierapp

  • 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
複製代碼
相關文章
相關標籤/搜索