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

你可能感兴趣的:(iOS 10 的 User Notifications)