iOS10 推送通知

iOS10新增加了一個UserNotificationKit(用戶通知框架)來整合通知相關的API,UserNotificationKit框架增加了很多令人驚喜的特性:

  • 更加豐富的推送內容:現在可以設置推送的title、subtitle、body 以及符合大小的圖片、音頻、視頻等附件內容。
  • 更好的通知管理:現在可以對通知進行查看、更新、刪除了。
  • 更優雅的展示方式:可以設置應用在前臺展示通知。

對iOS10而言,UserNotificationKit(用戶通知框架)是一種系統層面的UI展示,遠程通知APNS只是通知的一種觸發方式,UserNotificationKit的重要意義在于統一了Remote(遠程通知)和Local(本地通知)。

UserNotificationKit 基本流程

UserNotificationKit使用大概分為以下幾個過程:

  • 注冊通知: 獲取相關權限,注冊APNS
  • 發送通知: 創建通知并發起通知請求
  • 處理通知: 處理通知回調,對通知進行額外的操作

首先你需要向用戶請求通知權限,在取得權限后注冊通知;
然后你可以創建一個通知并發起推送,當然對于遠程推送APNS而言,你還需要注冊DeviceToken;
在接受到推送通知后可以根據app的運行情況決定是否展示通知,當然你也可以通過一系列的回調接口對通知進行處理加工。

注冊通知

請求權限

iOS 10 進一步消除了本地推送和遠程推送的區別,因為兩者在申請權限的時候都是在打斷用戶的行為。因此iOS 10統一了推送權限的申請:

//iOS8以下
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
//iOS8 - iOS10
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge categories:nil]];
//iOS10
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNAuthorizationOptions options = UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert;
[center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) {
    
}
//iOS10 swift 
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]{          granted, error in
}

推送設置

iOS 10 還可以實時獲取用戶當前的推送的設置:

//可以獲取的設置參數
@available(iOS 10.0, *) 
open class UNNotificationSettings : NSObject, NSCopying, NSSecureCoding {
    open var authorizationStatus: UNAuthorizationStatus { get }
    open var soundSetting: UNNotificationSetting { get }
    open var badgeSetting: UNNotificationSetting { get }
    open var alertSetting: UNNotificationSetting { get }
    open var notificationCenterSetting: UNNotificationSetting { get }
    open var lockScreenSetting: UNNotificationSetting { get }
    open var carPlaySetting: UNNotificationSetting { get }
    open var alertStyle: UNAlertStyle { get }
}
//獲取設置
UNUserNotificationCenter.current().getNotificationSettings {
    settings in 
    print(settings.authorizationStatus) // .authorized | .denied | .notDetermined
    print(settings.badgeSetting) // .enabled | .disabled | .notSupported
}

在有些情況下,可以對推送權限設置進行檢查。

APNS:DeviceToken

一旦獲得用戶權限后,你就可以在應用中發送本地通知了。不過對于APNS而言,你還需要申請DeviceToken,把DeviceToken傳遞給服務器,然后服務器根據DeviceToken發送到對應的用戶。

//向APNSS請求deviceToken:
UIApplication.shared.registerForRemoteNotifications()
//回調
func application(_ application: UIApplication,             didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {  
        print("Get Push token: \(deviceToken)")
}

發送通知

本地通知發送的基本流程

1.設置推送內容
2.設置通知觸發器
3.添加通知

1.設置推送內容

let content = UNMutableNotificationContent()
content.title = "Time Interval Notification" //推送內容標題
content.subtitle = "My first notification" //推送內容子標題
content.body = "My first notification"http:// 推送內容body
content.categoryIdentifier = "categoryIdentifier"  //category標識,操作策略

2.設置通知觸發器

有4種觸發器
UNPushNotificationTrigger 觸發APNS服務,系統自動設置(這是區分本地通知和遠程通知的標識)
UNTimeIntervalNotificationTrigger 一段時間后觸發
UNCalendarNotificationTrigger 指定日期觸發
UNLocationNotificationTrigger 根據位置觸發,支持進入某地或者離開某地或者都有。

//設置5秒后觸發
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

//每周三,13點觸發
var components:DateComponents = DateComponents()
components.weekday = 4;//周三
components.hour = 13;//13點
let triggerDateComponents = UNCalendarNotificationTrigger(dateMatching: components, repeats: true);

//這個點,100米范圍內,進入觸發。
let Coordinate2:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 39, longitude: 116);
let region:CLCircularRegion = CLCircularRegion(center: Coordinate2, radius: 100, identifier: "center")
region.notifyOnEntry = true;
region.notifyOnExit = false;
let triggerRegion = UNLocationNotificationTrigger(region: region, repeats: true);

3.添加通知

//先創建一個請求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
//將請求添加到發送中心
UNUserNotificationCenter.current().add(request) { error in
    if error == nil {
        print("Time Interval Notification scheduled: \(requestIdentifier)")
    }
}

APNS:Payload

iOS 10之前的推送內容比較單一,僅僅支持一段文字的消息推送,最好帶上badge和sound,以及一些自定義的字段內容,而在iOS 10中,Apple豐富了推送內容,并且增加了更多的顯示方式。

//iOS 10 之前的基礎Payload: 
{
  "aps":{
    "alert":{
        "body": "This is a message"
    },
    "sound":"default",
    "badge":1
  }
}
//iOS 10 基礎Payload:
{
  "aps":{
    "alert":{
      "title":"I am title",
      "subtitle":"I am subtitle",
      "body":"I am body"
    },
    "sound":"default",
    "badge":1
  }
}

title和subtitle并不是必須的。

處理通知

常規處理

在創建通知請求時,我們已經指定了標識符。iOS10中我們可以通過標識符來管理處理通知。通UserNotificationKIt的一系列API 你可以做到: 查找、更新和刪除通知。其中關鍵在于request的identifier,即在創建的時候的指定的通知的標識符。

查找通知

//獲取所有等待遞送的通知
UNUserNotificationCenter.current().getPendingNotificationRequests { (request) in

}
//獲取所有已經遞送的通知
UNUserNotificationCenter.current().getDeliveredNotifications { (noti) in

 }

更新通知

遠程推送可以進行通知的更新,在使用 center 的 addNotificationRequest:withCompletionHandler: 向 APNS提交請求時,在 HTTP/2 的 header 中 apns-collapse-id key 的內容將被作為該推送的標識符進行使用。多次推送同一標識符的通知即可進行更新。

//先創建一個請求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
//重新添加通知
UNUserNotificationCenter.current().add(request) { error in
    if error == nil {
        print("Time Interval Notification scheduled: \(requestIdentifier)")
    }
}

刪除通知/取消通知

//使用這個API來移除通知 還有一系列相似的API
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: dentifiers)
//取消還未展示的本地通知
UNUserNotificationCenter.current().add(request) { error in
    if error != nil {
        print("Notification request added: \(identifier)")
    }
}
delay(2) {
    let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
    let newRequest = UNNotificationRequest(identifier: identifier, content:newContent, trigger: newTrigger)
    UNUserNotificationCenter.current().add(newRequest) { error in
        if error != nil {
            print("Notification request updated: \(identifier)")
        }
    }
}

通知的回調:UNUserNotificationCenterDelegate

UNUserNotificationCenterDelegate提供了兩個方法處理通知。兩個方法分別對應如何在應用內展示通知,和收到通知響應時要如何處理的工作。

//在應用內展示通知
func userNotificationCenter(_ center: UNUserNotificationCenter, 
                       willPresent notification: UNNotification, 
                       withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 
    {
        completionHandler([.alert, .sound])

        // 如果不想顯示某個通知,可以直接用空 options 調用 completionHandler:
        // completionHandler([])
    }
//對通知進行響應,收到通知響應時的處理工作,用戶與你推送的通知進行交互時被調用;
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    completionHandler()
}

Notification Extension

iOS 10 中添加了很多 extension,作為應用與系統整合的入口。與通知相關的 extension 有兩個:Service Extension 和 Content Extension。前者可以讓我們有機會在「接收到推送之后、展示推送之前」對通知內容進行修改;后者可以用來自定義通知視圖的樣式。

Service Extension

Service Extension可以對推送進行處理,更改、替換原有的內容。他可以對通知內容進行加密,也可以給推送展示內容添加附件(比如照片、背景音樂),使得內容更加豐富。

//NotificationService 的模板已經為我們進行了基本的實現:
class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    // 1 讓你可以在后臺處理接收到的推送,傳遞修改后的的內容給 contentHandler,然后展示
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        if let bestAttemptContent = bestAttemptContent {
            if request.identifier == "mutableContent" {
                bestAttemptContent.body = "\(bestAttemptContent.body), onevcat"
            }
            contentHandler(bestAttemptContent)
        }
    }

    // 2 在在你獲得的一小段運行代碼的時間內沒有調用 contentHandler 的話,系統會調用這個方法。可以在這里傳肯定不會出錯的內容,或者他會默認傳遞原始的推送內容。
    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

Service Extension 現在只對遠程推送APNS的通知起效,你可以在推送 payload 中增加一個 mutable-content 值為 1 的項來啟用內容修改

{
  aps : {
    alert : "New Message",
    mutable-content : 1
  },
  encrypted-content : "#myencryptedcontent"
}

Media Attachments

iOS 10 的另一個亮眼功能是多媒體的推送,開發者能夠直接在推送的通知內容里面傳遞多媒體資源,在客戶端進行直接的展示,極大豐富了推送內容的可讀性和趣味性。

為本地通知添加添加多媒體內容

通過本地磁盤上的文件 URL 創建一個 UNNotificationAttachment 對象,然后將這個對象放到數組中賦值給 content 的 attachments 屬性:

let content = UNMutableNotificationContent()  
content.title = "Hey guys"
content.body = " body "
if let imageURL = Bundle.main.url(forResource: "image", withExtension: "jpg"),  
let attachment = try? UNNotificationAttachment(identifier: "imageAttachment", url: imageURL, options: nil)
{
    content.attachments = [attachment]
}

為遠程推送添加多媒體內容

只需要在推送的 payload 中指定需要加載的資源地址,這個地址可以是應用 bundle 內已經存在的資源,也可以是網絡的資源。不過因為在創建 UNNotificationAttachment 時我們只能使用本地資源,所以如果多媒體還不在本地的話,我們需要先將其下載到本地。在完成 UNNotificationAttachment 創建后,我們就可以和本地通知一樣,將它設置給 attachments 屬性,然后調用 contentHandler 了。

{
  "aps":{
    "alert":{
      "title":"Image Notification",
      "body":"Show me an image from web!"
    },
    "mutable-content":1 //表示我們會在接收到通知時對內容進行更改
  },
  "image": "https://img1.baidu.com.jpg"http://指明了目標圖片的地址 
}
  • 如果你想在遠程推送來的通知中顯示應用 bundle 內的資源的話,要注意 extension 的 bundle 和 app main bundle 并不是一回事兒。你可以選擇將圖片資源放到 extension bundle 中,也可以選擇放在 main bundle 里。總之,你需要保證能夠獲取到正確的,并且你具有讀取權限的 url。
  • UNNotificationContent 的 attachments 雖然是一個數組,但是系統只會展示第一個 attachment 對象的內容。不過你依然可以發送多個 attachments,然后在要展示的時候再重新安排它們的順序,以顯示最符合情景的圖片或者視頻。

Notification Actions

iOS 8 和 9 中 Apple 引入了可以交互的通知,這是通過將一簇 action 放到一個 category 中,將這個 category 進行注冊,最后在發送通知時將通知的 category 設置為要使用的 category 來實現的。這些action的API在iOS10 中被統一。

注冊Actions

private func registerNotificationCategory() {
    let saySomethingCategory: UNNotificationCategory = {
        // 1 創建 action 即一項交互操作 
        let inputAction = UNTextInputNotificationAction(
            identifier: "action.input",
            title: "Input", // 是交互按鈕的內容
            options: [.foreground], // options 可以讓該 action 成為一條可在前臺執行的 action
            textInputButtonTitle: "Send",
            textInputPlaceholder: "What do you want to say...")

        let goodbyeAction = UNNotificationAction(
            identifier: "action.goodbye",//,推送消息的其中的identifier
            title: "Goodbye",
            options: [.foreground])

        let cancelAction = UNNotificationAction( 
            identifier: "action.cancel",
            title: "Cancel",
            options: [.destructive])
        //2. 創建 category
        return UNNotificationCategory(identifier:"saySomethingCategory", actions: [inputAction, goodbyeAction, cancelAction], intentIdentifiers: [], options: [.customDismissAction])
    }()
    // 3 把 category 添加到通知中心:
    UNUserNotificationCenter.current().setNotificationCategories([saySomethingCategory])
}
// 4 觸發方式 
//遠程通知觸發方式
{
  aps : {
  alert : "Welcome to WWDC !",
  category : "saySomethingCategory" //和第二步identifier:"saySomethingCategory一致
         }
}
//本地通知觸發方式 Local Notifications 只需要在創建 contnet 的時候指定 和第二步identifier 即可
content.categoryIdentifier = "saySomethingCategory";

通過上面的三個步驟我們已經創建了一個category

作者:曲年
鏈接:http://www.lxweimin.com/p/567abab2098f
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • 本文發布在:iOS10 推送通知詳解(UserNotifications) iOS10新增加了一個UserNoti...
    曲年閱讀 9,203評論 2 22
  • 簡介 新框架 獲取權限 獲取用戶設置 注冊APNS,獲取deviceToken 本地推送流程 遠程推送流程 通知策...
    liuyanhongwl閱讀 20,532評論 35 51
  • iOS10里的通知與推送 通知文章2 遠程推送工具 一、各版本通知比較 iOS 8以后,APNs推送的字節是2k,...
    lxl125z閱讀 1,187評論 2 1
  • 這篇文章整理iOS10之后的推送通知(文中的推送通知,如不做特殊說明,默認是iOS10以后的推送通知) iOS10...
    王技術閱讀 3,557評論 5 10
  • 自律才更自由 今天分享的洗冷水澡,冷水不是冰水,只是低于體表溫度。之后熱風浴,排除身體濕氣。 回看今天的一個行動只...
    CSir205閱讀 72評論 0 0