SwiftUI - iOS10本地推送通知教程UserNotifications在Swift中的實現方式
阿新 • • 發佈:2020-06-09
## 簡介
訊息推送相信在很多人的眼裡都不陌生了吧?像即時聊天微信,好友發信息給你時會在頂部彈下小視窗提醒你。也像是在影院APP預訂了電影票,在開場前一小時你也會收到提醒。這類推送是需要經過後端傳送請求的,需要伺服器傳送推送請求,又或者使用如極光推送等第三方渠道。
那麼如果我們的APP不需要連網呢?這是不是就不能使用訊息推送了?不是的,蘋果還提供給我們本地訊息通知服務,即便APP不連網也能使用,功能也很強大可靠。本地時鐘的應用場景很廣泛,例如手機上的時鐘、日曆等。
那麼你知道如何去實現它嗎?這篇文章將告知你答案,同時以兩個小案例作為例子,以便更好地去理解它。
![](https://tva1.sinaimg.cn/large/007S8ZIlgy1gfmbj7acayj30yi0nsttv.jpg)
## 筆者環境
Xcode - Version 11.5 (11E608c)
Swift - version 5.2.4 (swiftlang-1103.0.32.9 clang-1103.0.32.53).
## 許可權獲取
UserNotifications 是 iOS10 推出來的框架,因此你只能在 10 或以上的版本使用它。推送服務和以往一樣,也是需要使用者授權的,當用戶同意後才能正常註冊訊息通知,當用戶拒絕時應該引導使用者去開啟APP的通知許可權。利用`requestAuthorization`方法彈出並獲取通知許可權,接收的引數`options`是具體的授權選項,一般有彈窗、未讀數量圖示和聲音即可,並在回撥閉包中可以獲取授權結果和錯誤。
```swift
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (status, err) in
if !status {
print("使用者不同意授權通知許可權")
return
}
}
```
status 為布林型別,true 表示使用者同意,false 即拒絕。在此種情況下,我們可以使用彈窗去引導使用者去開啟通知許可權,需要明確告知使用者開啟後有什麼好處,如果關閉會造成什麼影響等等。如果讓使用者手動開啟設定,找到APP,為APP開啟許可權,這樣未免太過複雜,所幸的是可以通過以下程式碼為使用者直接跳轉至該應用的許可權設定中心。
```swift
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, completionHandler: nil)
}
```
> 應彈窗提示使用者,待使用者同意後才跳轉至設定,不然容易引起使用者的不滿心理。
## 觸發器
本地訊息通知一般有以下三種類型的觸發器,它們都是繼承於類`UNNotificationTrigger`:
1. UNTimeIntervalNotificationTrigger - 在經過特定的時間後觸發本地訊息推送;
2. UNCalendarNotificationTrigger - 在特定的時間點觸發本地訊息推送;
3. UNLocationNotificationTrigger - 在進入或離開特定的地理位置時觸發本地訊息推送。
### UNTimeIntervalNotificationTrigger
手機上的時鐘用過吧,裡面的計時器功能就可以用`UNTimeIntervalNotificationTrigger`實現,比如開始計時30分鐘,那麼在計時器完成的時候就是使用通知提醒。
那麼設定在經過特定的時間後觸發本地訊息推送,一般都經由以下幾個步驟:
1. 首先建立`UNMutableNotificationContent`類,設定標題和內容,如果你有子標題還可以設定子標題,一般很少見到會設定子標題的應用。
2. 建立觸發器,這裡就是`UNTimeIntervalNotificationTrigger`,設定執行秒數和是否迴圈通知。
3. 建立通知請求`UNNotificationRequest`,這裡需要指定通知的`identifier`,內容和觸發器,至於`identifier`,你可以隨意定義。
4. 最後將通知請求新增到系統的通知中心`UNUserNotificationCenter`即可。
例子,建立一個通知,在5秒後執行訊息推送。例項程式碼展示如下:
```swift
let content = UNMutableNotificationContent()
content.title = "新增朋友 對著月亮敲程式碼"
//content.subtitle = "子標題"
content.body = "公眾號 gh_6a83a7c19315"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "Notification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { err in
err != nil ? print("新增本地通知錯誤", err!.localizedDescription) : print("新增本地通知成功")
}
```
有一處小 Tips,`UNTimeIntervalNotificationTrigger`建立時的`repeats`選項,如果你設定為迴圈通知時,即需要每隔`N`秒觸發一次通知,那麼你必須至少設定為60秒的時間間隔,如若低於60秒,你將會得到這樣一條錯誤。
```
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'time interval must be at least 60 if repeating'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48
2 CoreFoundation 0x00007fff23c70ff8 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff256e9b51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 UserNotifications 0x00007fff2c7dfc7c -[UNTimeIntervalNotificationTrigger _initWithTimeInterval:repeats:] + 277
```
### UNCalendarNotificationTrigger
手機上的日曆用過吧,在新建日程的時候,你可以選擇一個提醒時間,這樣它就會在你設定的提醒時間提醒你,這種情況就很適合用`UNCalendarNotificationTrigger`去實現。
舉個例子,我們要在每晚7點提醒使用者看公眾號。
```swift
let content = UNMutableNotificationContent()
content.title = "新增朋友 對著月亮敲程式碼"
//content.subtitle = "子標題"
content.body = "公眾號 gh_6a83a7c19315"
content.badge = 1
let dateComponents = DateComponents(hour: 19) // 1
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) // 2
let request = UNNotificationRequest(identifier: "Notification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { err in
err != nil ? print("新增本地通知錯誤", err!.localizedDescription) : print("新增本地通知成功")
}
```
> 1 - 建立時間元件,19點即為晚上7點
>
> 2 - 建立UNCalendarNotificationTrigger物件,並將dateComponents賦值到dateMatching,repeats為true,重複在每天19點收到通知提醒。
### UNLocationNotificationTrigger
這個觸發器不在此篇文章講述,留給你們自己去實現和測試結果。
## 圖示
![](https://tva1.sinaimg.cn/large/007S8ZIlgy1gfmbjc79n3j30yi0bhhby.jpg)
還記得剛剛設定的屬性`badge`嗎,我們設定值為`1`,這意味著在iPhone桌面上的應用圖示在收到通知時,右上角圓點內所展示的數字就是`badge`的值。
這個屬性值是`applicationIconBadgeNumber`,它是`UIApplication`的屬性,設定為0即為隱藏,預設也是0。
```
UIApplication.shared.applicationIconBadgeNumber = 0
```
## 訊息推送回調代理
接收使用者對訊息推送的反饋事件,比如說應用在後臺收到了通知,使用者點選了這條通知進入到了APP裡面,我們需要獲取這個事件去做一些處理,比如跳去某個介面,這裡例子不講這麼複雜,只通過簡單地判斷使用者是通過哪個通知進來的。
接收回調代理事件前,需要遵循`UNUserNotificationCenterDelegate`協議,並設定`delegate`接收的物件。
```swift
extension AppDelegate: UNUserNotificationCenterDelegate {}
UNUserNotificationCenter.current().delegate = self
```
在`Swift`語言中,可以通過`extension`擴充套件類遵循的協議,並在`extension`。
當應用在前臺執行時,收到的是這個`-userNotificationCenter:willPresentNotification:withCompletionHandler:`代理方法。`UNNotification`物件儲存了傳遞到應用的一些資料,通過此物件可以拿到此條通知關聯的觸發器`notification.request.trigger`,從而判斷其型別。
```swift
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
guard let trigger = notification.request.trigger else { return; }
if trigger.isKind(of: UNTimeIntervalNotificationTrigger.classForCoder()) {
print("Notification did receive, Is class UNTimeIntervalNotificationTrigger")
} else if trigger.isKind(of: UNCalendarNotificationTrigger.classForCoder()) {
print("Notification did receive, Is class UNCalendarNotificationTrigger")
}
}
```
當應用在後臺,或者被殺死的狀態下,收到的是這個`-userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:`代理方法。此方法接收`UNNotificationResponse`型別的引數,它裡面包含`notification`屬性,因此可以參考上面的程式碼進行觸發器的判斷。
```swift
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
guard let trigger = response.notification.request.trigger else { return; }
if trigger.isKind(of: UNTimeIntervalNotificationTrigger.classForCoder()) {
print("Notification did receive, Is class UNTimeIntervalNotificationTrigger")
} else if trigger.isKind(of: UNCalendarNotificationTrigger.classForCoder()) {
print("Notification did receive, Is class UNCalendarNotificationTrigger")
}
}
```
## 總結
1. 本地通知有三種類型的觸發器,分別是UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger和UNLocationNotificationTrigger。
2. UNTimeIntervalNotificationTrigger在設定迴圈通知時,所設定的時間隔不能低於60秒,否則會報執行時錯誤。
## 往期回顧
1. [SwiftUI - 一起來仿寫微信APP之一首頁列表檢視](https://www.cnblogs.com/GarveyCalvin/p/swiftui-fake-wechat-list.html)
2. [SwiftUI - 一步一步教你使用UIViewRepresentable封裝網路載入檢視(UIActivityIndicatorView)](https://www.cnblogs.com/GarveyCalvin/p/representtable-uikit.html)
## Demo 原始碼下載
我已經把 Demo 上傳至 GitHub 上面,專案名字是 [SwiftUI-Tutorials](https://github.com/GarveyCalvin/SwiftUI-Tutorials),目錄名為`GCLocalUserNotification`,有需要的朋友可以去下載執行一下,當然你也可以跟著文章去做一遍,這樣更有利於你掌握此方面的知識。
**如果本文章對你有幫助,請關注我,你的關注就是我後續寫文章的動力,下期會更精彩噢!**
## 關於作者
博文作者:GarveyCalvin
微博:https://weibo.com/feiyueharia
部落格園:https://www.cnblogs.com/GarveyCalvin
本文版權歸作者,歡迎轉載,但必須保留此段宣告,並給出原文連結,謝謝合作!
### 公眾號
作者第一次運營公眾號,請你們一定要關注我的公眾號,給我點動力,後期主要運營公眾號為主。這是第三篇釋出的文章,需要你們的支援,謝謝你們!
![](https://tva1.sinaimg.cn/large/007S8ZIlgy1gdy28erjq7j305z05z0t2.jpg)
### 微信群
佛系等待你們的到來,若二維碼過期,請加我QQ:1147626297,記得寫備註,我重新發連結給你。快來加入我的“億”個人群吧!
![](https://tva1.sinaimg.cn/large/007S8ZIlgy1gfmbz7gi0oj307i09qd