iOS 10 的 User Notifications

一、新功能:

  • Media attachments:可以添加音频,视频和图片了
  • Notification Content extensions:可以自定义通知界面了
  • Managing notifications:用户可以在通知界面管理通知了
  • Notification Service app extensions:预处理远程通知的payloads

二、本地通知

框架的核心是UNUserNotificationCenter,通过单例获取。
步骤:

  1. import UserNotifications
  2. viewDidLoad 请求授权
    UNUserNotificationCenter.current()
        .requestAuthorization(options: [.alert, .sound]) {
            (granted, error) in
            if granted {
                self.loadNotificationData()
            } else {
                print(error?.localizedDescription)
            }
    }
  1. scheduleRandomNotification设定通知
// 1
    let content = UNMutableNotificationContent()
    content.title = "New cuddlePix!"
    content.subtitle = "What a treat"
    content.body = "Cheer yourself up with a hug 🤗 "
    //TODO: Add attachment
    // 2
    let trigger = UNTimeIntervalNotificationTrigger(
        timeInterval: seconds, repeats: false)
    
    // 3
    let request = UNNotificationRequest(
        identifier: randomImageName, content: content, trigger: trigger)
    // 4
    UNUserNotificationCenter.current().add(request, withCompletionHandler:
        { (error) in
            if let error = error {
                print(error)
                completion(false)
            } else {
                completion(true)
            }
    })
  1. 运行,进入background

三、添加附件

    let attachment = try! UNNotificationAttachment(identifier:
        randomImageName, url: imageURL, options: .none)
    content.attachments = [attachment]

运行,进入background,用3D touch 或 下拉查看大图

四、前台通知

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler:
        @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler(.alert)
    }
}

AppDelegateapplication(_:didFinishLaunchingWithOptions:)

    UNUserNotificationCenter.current().delegate = self

运行,不进入background

五、查询通知

loadNotificationData中:

    // 1
    let notificationCenter = UNUserNotificationCenter.current()
    let dataSaveQueue = DispatchQueue(label:
        "com.raywenderlich.CuddlePix.dataSave")
    // 2
    group.enter()
    // 3
    notificationCenter.getNotificationSettings { (settings) in
        let settingsProvider = SettingTableSectionProvider(settings:
            settings, name: "Notification Settings")
        // 4
        dataSaveQueue.async(execute: {
            self.tableSectionProviders[.settings] = settingsProvider
            group.leave()
        })
    }
 
    group.enter()
    notificationCenter.getPendingNotificationRequests { (requests) in
        let pendingRequestsProvider =
            PendingNotificationsTableSectionProvider(requests:
                requests, name: "Pending Notifications")
        dataSaveQueue.async(execute: {
            self.tableSectionProviders[.pending] = pendingRequestsProvider
            group.leave()
        })
    }
    group.enter()
    notificationCenter.getDeliveredNotifications { (notifications) in
        let deliveredNotificationsProvider =
            DeliveredNotificationsTableSectionProvider(notifications:
                notifications, name: "Delivered Notifications")
        dataSaveQueue.async(execute: {
            self.tableSectionProviders[.delivered]
                = deliveredNotificationsProvider
            group.leave()
        })
    }
    ```
在`AppDelegate`的`userNotificationCenter(_:willPresent:withCompletionHandler:)`
    NotificationCenter.default.post(name:
        userNotificationReceivedNotificationName, object: .none)
* 通知的状态: Pending, Delivered, Deleted

### 六、修改,删除通知
* 修改通知:用相同的`identifier`创建新的`UNNotificationRequest`,内容不同。
* 删除通知
// 1
guard let section =
    NotificationTableSection(rawValue: indexPath.section),
    editingStyle == .delete && section == .pending else { return }
// 2
guard let provider = tableSectionProviders[.pending]
    as? PendingNotificationsTableSectionProvider else { return }
let request = provider.requests[indexPath.row]
// 3
UNUserNotificationCenter.current()
    .removePendingNotificationRequests(withIdentifiers:
        [request.identifier])
loadNotificationData(callback: {
    self.tableView.deleteRows(at: [indexPath], with: .automatic)
})

### 七、 自定义通知界面 - Notification content extensions
 1. 不能传递手势,可以响应actions
 2. New a target,命名为ContentExtension
 3. 画界面
 4. 在 `didReceive(_:)` 方法:
    // 1
    guard let attachment = notification.request.content.attachments.first
        else { return }
    // 2
    if attachment.url.startAccessingSecurityScopedResource() {
        let imageData = try? Data.init(contentsOf: attachment.url)
        if let imageData = imageData {
            imageView.image = UIImage(data: imageData)
        }
        attachment.url.stopAccessingSecurityScopedResource()
    }
 5. 当通知来了,系统需要知道传递给哪一个extension - 配置Info.plist,和通知请求的content.categoryIdentifier设置相同的值 。

原理:通知触发了,系统查看是否设定通知请求的content.categoryIdentifier,如果没有,使用默认的界面。
如果设定了,启动相应的extension自定义的界面。
// 本地通知在Info.plist配置
{ UNNotificationExtensionCategory : newCuddlePix }
// 远程通知在payload配置
{“category":"newCuddlePix"}
// 通知请求的content
content.categoryIdentifier = newCuddlePix

 6. 隐藏系统默认界面: 在 `content extension` 的`Info.plist`中添加

{ UNNotificationExtensionDefaultContentHidden : true }

 7. Handling notification actions
  * 交互性 - 通过自定义 custom action 
  * 主要的类:UNNotificationCategory
  * 例如:邀请通知,同意或者拒绝
  * 通过identifier区分,在`AppDelegate`中:
func configureUserNotifications() {
    // 1
    let starAction = UNNotificationAction(identifier:
        "star", title: "* star my cuddle * ", options: [])
    // 2
    let category =
        UNNotificationCategory(identifier: newCuddlePixCategoryName,
                               actions: [starAction],
                               intentIdentifiers: [],
                               options: [])
    // 3
    UNUserNotificationCenter.current()
        .setNotificationCategories([category])
}
在`AppDelegate`的`application(_:didFinishLaunchingWithOptions:)`

configureUserNotifications()

  * 运行,点击action,没有实现,则dismiss
  * 在extension中,添加代码:
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
    // 1
    if response.actionIdentifier == "star" {
        // TODO Show Stars
        let time = DispatchTime.now() +
            DispatchTimeInterval.milliseconds(2000)
        DispatchQueue.main.asyncAfter(deadline: time) {
            // 2
            completion(.dismissAndForwardAction)
        }
    }
}
  * 添加框架到extension  : 选中target,然后:

imageView.showStars()

  * 通知中心代理(AppDelegate)中:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    print("Response received for \(response.actionIdentifier)")
    completionHandler()
}
* 方法执行顺序
     1. userNotificationCenter(_:willPresent:withCompletionHandler:)  :是否前台显示
     2. didReceive(_:) : 显示自定义界面
     3. didReceive(_:completionHandler:) : extension 处理 action
     4. userNotificationCenter(_:didReceive:withCompletionHandler:) :通知中心代理处理 action

### 八、 远程通知
原理:
![APNS.png](http://upload-images.jianshu.io/upload_images/1510300-0057dc11b2e02078.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
作用: 预先处理媒体附件,解密内容
步骤:
1. 在`General`中,确定`ContentExtension` 的`bundle id` 前缀和app相同,选择Team
2. 在`Capabilities`打开 `Push Notifications`
3. 获取远程通知证书:[教程](https://www.raywenderlich.com/123862)
4. 获取 `Device Push Token`:在`AppDelegate`的`application(_:didFinishLaunchingWithOptions:)`

// 注册 - 只有在真实设备中起作用
application.registerForRemoteNotifications()

// APNS 响应
extension AppDelegate {
// 1
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Registration for remote notifications failed")
print(error.localizedDescription)
}
// 2
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registered with device token: (deviceToken.hexString)")
}
}

5. 下载推送软件:[Pusher](https://github.com/noodlewerk/NWPusher)
 * `Pusher` 导入 `证书` 或者 `p12`文件,填写 `Device Push Token`, `payload` 填写以下内容:

{
"aps":{
"alert":{
"title":"New cuddlePix!",
"subtitle":"From your friend",
"body":"Cheer yourself up with this remote hug ) "
},
"category":"newCuddlePix"
}
}


### 九、 Notification Service app extensions
 作用:预处理远程通知的payloads
1. payload添加附件

{
"aps":{
"alert":{
"title":"New cuddlePix!",
"subtitle":"From your friend",
"body":"Cheer yourself up with this remote hug ) "
},
"category":"newCuddlePix",
"mutable-content": 1
},
"attachment-url": "https://wolverine.raywenderlich.com/books/i10t/
notifications/i10t-feature.png"
}

 * mutable-content : 通知是否能被 service extension 改变,默认为0
 * attachment-url : 附件资源
2. 创建名为`ServiceExtension` 的target

import MobileCoreServices

在`didReceive(_:withContentHandler:)`中:

// 1
guard let attachmentString = bestAttemptContent
.userInfo["attachment-url"] as? String,
let attachmentUrl = URL(string: attachmentString) else { return }
// 2
let session = URLSession(configuration:
URLSessionConfiguration.default)
let attachmentDownloadTask = session.downloadTask(with:
attachmentUrl, completionHandler: { (url, response, error) in
if let error = error {
print("Error downloading: (error.localizedDescription)")
} else if let url = url {
// 3
let attachment = try! UNNotificationAttachment(identifier:
attachmentString, url: url, options:
[UNNotificationAttachmentOptionsTypeHintKey: kUTTypePNG])
bestAttemptContent.attachments = [attachment]
}
// 5
contentHandler(bestAttemptContent)
})
// 4
attachmentDownloadTask.resume()

3. 运行`Pusher`,推送payload
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 介绍一下iOS10的通知新功能,用户体验的提升和开发者能够发挥的地方非常多,使得iOS更具有竞争力。 1.iOS ...
    F麦子阅读 3,787评论 3 4
  • 前言在今年6月14号苹果WWDC开发者大会上,苹果带来了新的iOS系统——iOS 10。苹果为iOS 10带来了十...
    sky_kYU阅读 3,443评论 5 12
  • <extension>123</extension><mime-type>application/vnd.lotu...
    张不二01阅读 964评论 0 0
  • 你那颗心是否 永在这江边沉睡 我这份情真心 仍在这独醉 你这杯酒让我 思恋已久 教人醉魅 却更愁断豪睡 教涛涛迷人...
    醉心淼阅读 314评论 0 0
  • 1、人之所以有一张嘴,而有两只耳朵,原因是听的要比说的多一倍。 2、哲人无忧,智者常乐。并不是因为所爱的一切他都拥...
    彩霞漫天阅读 221评论 0 1