macOS 中的消息推送分為本地消息通知和遠程消息通知,本文十里將介紹一下本地消息通知,展示一些常規的使用方法,方便大家了解本地消息推送的實現過程。

開發平台

  • macOS 10.14.3
  • swift 4.2.1
  • xcode 10.1

1. 簡單介紹

本地消息通知(Notification)是由 App 請求用戶消息中心(User Notifications Center)而推送的,我們的 App 既是消息的提供者又是消息的接受者,一個 App 最多支持 64 個消息通知!

1.1 消息通知組成

一個消息通知一般在顯示上看由標題(title)、副標題(subtitle)、消息內容、logo和按鈕組成。另外還包括提示音的設置和用戶信息(用於傳遞數據)。

1.2 消息通知的類型

消息通知主要有三種類型:無、橫幅(barner)和提示(alert)

默認情況下使用 橫幅 樣式!

1.3 通知推送的觸發條件

消息推送的觸發條件有多種方式,消息中心會根據指定的觸發條件推送消息:

  • 直接推送通知,一般是根據 App 中自身邏輯靈活手動觸發
  • 按照指定時間間隔推送通知,可以設置為重複或者不重複
  • 根據指定的日期時間推送通知,可以設置是否重複
  • 根據地理位置推送通知,通常要設置經緯度坐標以及範圍(方圓距離)

1.4 消息通知的處理

本地消息通知通過用戶消息通知中心的消息隊列進行管理,根據通知的觸發條件推送的消息會依次存放到這個消息隊列,然後依次通知給 App,而 App 可以通過對消息中心的代理實現代理方法對消息通知行為進行相應處理。

1.5 消息通知的管理

消息通知的管理由通知中心完成,包括消息通知的註冊、刪除、推送以及許可權。

1.5.1 消息通知的註冊

要想讓 App 配置好的通知能夠按照預期進行推送,必須要將通知註冊到通知中心。

1.5.2 消息通知的推送

通知中心會時刻監聽著每個消息通知,一旦滿足觸發條件就會像消息通知隊列中推送消息。

1.5.3 消息通知的刪除

有時需要關閉已經註冊的消息通知的推送活動,通知中心可以將指定消息通知刪除,不再監管。

1.5.4 消息通知的許可權

在 macOS 的系統偏好設置中可以設置指定應用的通知許可權:

這些通知許可權在 App 中可以由通知中心進行管理,通常就是消息彈窗和聲音播放兩點。

1.6 關於實現

上面說了本地消息通知的一些基本概念和簡單介紹,那麼實現本地消息通知的流程就很清楚了:

  • 消息通知的配置,包括觸發條件、消息通知中內容
  • 註冊消息通知到消息通知中心
  • 實現消息通知中心的代理方法,從而完成對消息的處理

目前看 Apple 官方的開發介面有兩套:

  • 傳統的消息通知介面,在 Foundation 框架中實現,SDK 支持明確標記 macOS 10.8–10.14 Deprecated,也就是說 10.14 之後便廢棄了
  • 最新的消息通知介面,與多個軟體平台(iOS、watchOS、tvOS)共用,使用 UserNotifacations 框架,SDK 支持
    • iOS 10.0+
    • macOS 10.14+
    • tvOS 10.0+
    • watchOS 3.0+

不難看出 Apple 的軟體開發生態藍圖的宏大,這不在本文討論範圍內!在下面會以一個簡單的例子介紹使用兩套方法的實現過程,不過會著重講新的介面!

2. 示例

下面我們通過兩個簡單的 Demo 看一下如何實現本地消息通知。兩個 Demo 中我們分別設置一個按鈕,並分別綁定 各自的 Action,一個 Action 中按照傳統的方式實現消息通知,另一個 Action 中按照新的方式實現。這個消息通知具有以下實現:

  • 立馬推送的
  • 使用系統聲音作為提示音,消息推送時播放
  • 通知攜帶數據在用戶信息中
  • 通知設置兩個按鈕,一個是關閉一個是確定按鈕,點擊後列印傳遞的用戶信息

2.1 傳統實現方式

  • 打開 Xcode 新建一個使用 storyboard 的工程,我們就命名為 OldNotificationDemo
  • 打開 Main.storyboard ,在 view controller 中添加一個按鈕,按鈕標題改為 通知
  • 為兩個按鈕綁定 action,在 Main.storyboard 中按下快捷鍵 Option + Command + 回車鍵 打開輔助編輯器,按住 ctrl 鍵的同時滑鼠左鍵拖動按鈕到 ViewController.swift 的 ViewController 類中綁定 action 為 oldNotificationAction

2.1.1 更改通知樣式

傳統介面下,App 默認使用橫幅的通知樣式,但是橫幅的通知只能顯示 reply button 和 other button,但是我們想自己定義一個按鈕,只能使用提示的樣式,所以我們首先更改一下通知樣式,需要在 Info.plist 文件中添加一個新的鍵——NSUserNotificationAlertStyle,值設置為 alert:

2.1.2 oldNotificationAction 實現

下面在 oldNotificationAction 中添加使用傳統方法消息通知的實現:

@IBAction func oldNotificationAction(_ sender: Any) {
let userNotification = NSUserNotification()

userNotification.title = "傳統方式"
userNotification.subtitle = "old"
userNotification.informativeText = "我是一個傳統的方式"

userNotification.hasActionButton = true
userNotification.otherButtonTitle = "關閉"
userNotification.actionButtonTitle = "顯示"

userNotification.identifier = "OLD_NOTIFICATION_DEMO"
userNotification.userInfo = ["method": "old"]

userNotification.soundName = NSUserNotificationDefaultSoundName

NSUserNotificationCenter.default.delegate = self
NSUserNotificationCenter.default.deliver(userNotification)
}

  • 上面使用消息通知中心的 deliver 方法直接推送消息,如果要設置其它觸發方式的通知需要使用通知中心的 scheduleNotification 方法
  • 將要傳遞用戶數據設置在 userNotificationuserInfo
  • 設置了通知中心的代理為 self,所以要完成剩下的實現,還需要實現代理方法
  • 要顯示 action 按鈕必須設置 userNotificationhasActionButtontrue

2.1.3 實現代理方法

extension ViewController: NSUserNotificationCenterDelegate {

// 當 App 在前台時是否彈出通知
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
return true
}

// 推送消息後的回調
func userNotificationCenter(_ center: NSUserNotificationCenter, didDeliver notification: NSUserNotification) {
print("(Date(timeIntervalSinceNow: 0)) -> 消息已經推送")
}

// 用戶點擊了通知後的回調
func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
switch notification.activationType {
case .actionButtonClicked:
let method = notification.userInfo!["method"] as! String
print("methods -> (method)")
case .contentsClicked:
print("clicked")
case .replied:
print("replied")
case .additionalActionClicked:
print("additional action")
default:
print("action")
}
}

}

  • 代理方法 userNotificationCenter(:shouldPresent:)->Bool 中如果返回 false,那麼 App 的窗口是當前系統桌面顯示的窗口,就不會彈出通知也不會播放提示音
  • 通知中心推送消息後會調用 userNotificationCenter(:didDeliver:)
  • 當用戶操作彈窗時,比如點擊彈窗、點擊彈窗上的按鈕時,userNotificationCenter(:didActivate) 方法就會被調用,在其中要實現對各種操作的處理

2.1.4 運行程序

運行程序後點擊窗口中按鈕,不出意外就會看到通知彈窗,同時控制台會列印推送消息,點擊彈窗的按鈕或彈窗可以看到控制台列印了相應的信息!

Demo 下載:

OldNotificationDemo?

pichome-1254392422.cos.ap-chengdu.myqcloud.com

2.2 新的實現方式

  • 打開 Xcode 新建一個使用 storyboard 的工程,我們就命名為 NotificationDemo
  • 打開 Main.storyboard ,在 view controller 中添加一個按鈕,按鈕標題改為 通知
  • 為兩個按鈕綁定 action,在 Main.storyboard 中按下快捷鍵 Option + Command + 回車鍵 打開輔助編輯器,按住 ctrl 鍵的同時滑鼠左鍵拖動按鈕到 ViewController.swift 的 ViewController 類中綁定 action 為 notificationAction

2.2.1 關於樣式

新的實現方式與傳統的實現方式不同的是,在樣式為橫幅(barner)時,將滑鼠放置在通知彈窗上可以顯示自定義的 action 按鈕,所以這裡沒必要更改樣式!

2.2.2 notificationAction 實現

因為新的方式使用的是 UserNotifications 框架,所以需要先導入模塊,在 ViewController.swift 中添加代碼:

import UserNotifications

新的方式通知的實現過程與傳統的有很大不同,流程大概是:

  • 先創建一個 UNMutableNotificationContent 實例來設置通知的內容,包括標題、內容、圖片、標識符、提示聲音以及用戶數據
  • (可選) 創建一個觸發器,觸發器的類型有很多:
    • UNCalendarNotificationTrigger: 通過指定日期和時間進行觸發
    • UNTimeIntervalNotificationTrigger: 通過設置指定時間間隔和是否重複來觸發
    • UNLocationNotificationTrigger: 通過指定地理坐標及地域範圍來觸發
  • (可選) 創建操作集合,這個操作集合類型為 UNNotificationCategory 對應通知彈窗的按鈕,集合中元素為 UNNotificationAction 實例,需要調用通知中心的 setNotificationCategories 方法添加生效。
    • barner樣式下直接顯示兩個操作項按鈕
    • alert 樣式下集合下的操作項會顯示為 操作 的子項
    • UNNotificationAction 創建時需要指定唯一標識符、顯示名稱和選項,標識符用於後期區分 action 進行操作處理
    • 集合的唯一標識符與通知內容實例的唯一標識符統一起來時,才能在 barner 樣式下顯示按鈕
  • 然後通過上面創建好的通知內容實例和觸發器創建一個通知請求,它是 UNNotificationRequest 實例,還需要指定一個唯一標識符,另外如果指定的觸發器為空,通知中心會立即推送通知
  • 最後指定通知中心的代理 實例,一般情況就是類自身即 self,之後調用通知中心實例的 add 方法將通知請求添加到通知中心實例,這個通知中心實例使用系統當前的就可以,調用 UNUserNotificationCenter.current() 即可獲得

所以根據我們的實現目標,notificationAction 的實現如下:

@IBAction func notificationAction(_ sender: Any) {
let content = UNMutableNotificationContent()
content.title = "新的方式"
content.body = "我是一個新的方式"

content.userInfo = ["method": "new"]

content.sound = UNNotificationSound.default
content.categoryIdentifier = "NOTIFICATION_DEMO"

let acceptAction = UNNotificationAction(identifier: "SHOW_ACTION", title: "顯示", options: .init(rawValue: 0))
let declineAction = UNNotificationAction(identifier: "CLOSE_ACTION", title: "關閉", options: .init(rawValue: 0))
let testCategory = UNNotificationCategory(identifier: "NOTIFICATION_DEMO",
actions: [acceptAction, declineAction],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: "",
options: .customDismissAction)

let request = UNNotificationRequest(identifier: "NOTIFICATION_DEMO_REQUEST",
content: content,
trigger: nil)

// Schedule the request with the system.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.setNotificationCategories([testCategory])
notificationCenter.add(request) { (error) in
if error != nil {
// Handle any errors.
}
}
}

注意:

testCategorycontent 一定要使用一致的標識符,否則通知橫幅樣式下不會顯示 action 按鈕

2.2.3 代理方法的實現

直接貼出實現:

extension ViewController: UNUserNotificationCenterDelegate {

// 用戶點擊彈窗後的回調
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
switch response.actionIdentifier {
case "SHOW_ACTION":
print(userInfo)
case "CLOSE_ACTION":
print("Nothing to do")
default:
break
}
completionHandler()
}

// 配置通知發起時的行為 alert -> 顯示彈窗, sound -> 播放提示音
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound])
}
}

  • 兩個回調中都有一個逃逸閉包參數,是一個完成處理的回調,一定要執行相應的 completionHandler
  • userNotificationCenter(:willPresent:withCompletionHandler) 方法中通過 completionHandler 配置通知行為,這裡配置既顯示彈窗又播放提示音

2.2.4 關於消息通知許可權

其實嚴格來講,一個 app 在第一次啟動的時候要向系統請求設置通知許可權的,等在之後的所有啟動的時候就不需要請求設置許可權了,只需每次讀取系統偏好設置中的許可權配置來實現相應的通知行為。貌似在 macOS 中不做這個操作,目前也沒什麼影響,如果想進一步了解許可權可以閱讀

Asking Permission to Use Notifications?

developer.apple.com
圖標

2.2.5 驗證實現

運行程序,點擊窗口中的按鈕就能看到通知了!將滑鼠放在通知上,就能顯示操作按鈕,點擊按鈕就能在 xcode 控制器窗口看到相應的列印信息了。

Demo 下載:

NotificationDemo?

pichome-1254392422.cos.ap-chengdu.myqcloud.com

總結

到目前為止,我們嘗試了兩種方式實現消息通知的推送,如果有其它的實現需求,比如按日期時間推送消息,直接參考閱讀 Apple 官方的資料吧!Apple 的各大系統平台介面融合是大勢所趨,如果有必要,大家趕快根據新的實現方式替換馬上要淘汰的方法吧!

參考

User Notifications in macOS?

blog.gaelfoppolo.com

Foundation | Apple Developer Documentation?

developer.apple.com

UserNotifications | Apple Developer Documentation?

developer.apple.com


博客原文:

macOS 開發之本地消息通知?

www.smslit.top
圖標

推薦閱讀:
相关文章