# iOS开发之iOS10推送必看(高阶2)

语言：zh-CN
原文：[iOS开发之iOS10推送必看(高阶2)](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao65>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao65>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-10-12-ios-10-notifications-part-2-service-extensions/>)
发布于: 2016-10-12T00:00:00+08:00
内容更新于: 2016-10-12T00:00:00+08:00
署名：徐不同

介绍 Notification Service Extension 在通知展示前处理内容的流程，说明创建扩展目标、修改文字和加载附件的方式，以及处理超时后回退原始通知的行为。

原文署名徐不同，Bison 编辑收录。

[原文所列来源](<http://www.jianshu.com/p/f77d070a8812>)

**阅读前** 这是 2015–2017 年间的技术记录，本次整理未重新运行验证示例。原文面向 iOS 10 引入的 Notification Service Extension。

> 更新晚啦，我要先跟大家说声抱歉哈~

如果大家还没有看我的这两篇文章，建议还是先阅读一下，循序渐进么~文章链接如下: [iOS开发 iOS10推送必看(基础篇)](<http://www.jianshu.com/p/f5337e8f336d>)  
 [iOS开发 iOS10推送必看(高阶1)](<http://www.jianshu.com/p/3d602a60ca4f>)  
 这次的最后，终于有demo咯~。

编辑：[Bison](<https://allluckly.cn/>)  
 来源：[徐不同](<http://www.jianshu.com/p/f77d070a8812>)

> 在这篇文章，我会给大家讲一讲更高级一点的，定制化更高的远程通知。其中会补充我之前没讲的远程推送（多媒体）通知，以及UNNotificationServiceExtension，UNNotificationContentExtension，UNNotificationAction的相关类。相信大家看了这篇文章，虽不能说对苹果的远程推送了如指掌，但是也可以做一些基本的扩展咯~

## 1、UNNotificationServiceExtension

### 1.1、UNNotificationServiceExtension简介

UNNotificationServiceExtension是iOS10推出后的一个新特性，先看这张图：

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/1.png>)

从这张图上，我们可以看到，原先直接从APNs推送到用户手机的消息，中间添加了ServiceExtension处理的一个步骤（当然你也可以不处理），通过这个ServiceExtension，我们可以把即将给用户展示的通知内容，做各种自定义的处理，最终，给用户呈现一个更为丰富的通知，当然，如果网络不好，或者附件过大，可能在给定的时间内，我们没能将通知重新编辑，那么，则会显示发过来的原版通知内容。

那么ServiceExtension可以做什么呢？它的意义是什么呢？我总结了几点：

1、 安全

安全总是摆在第一位的，从iOS9开始，苹果鼓励我们使用更为安全的https协议可以看的出来，苹果公司是对安全很重视的一家公司，为什么在这里我会提到安全呢？是因为之前我们的推送内容，不管是通过第三方，还是通过苹果自带的通知处理，如果让有心人对数据做一次拦截，抓个包啥的，我们推送的内容就会完全暴露，当然有的同学说，我可以加密啊~但是不知道大家有没有想过，如果数据加密，那通知栏会怎么展示呢？（你千万别跟我说你把所有的远程推送变成本地通知。。）通过此次这次增加的UNNotificationServiceExtension的类，便可以更好的帮助我们实现数据的加密。 它的原理便是在收到通知后的最多30s内，你可以把你的通知内容，解密后，在重新展示在用户的通知拦上。

2、 内容的丰富

之前的通知展示内容比较少，以至于被各种广告提醒占据了。这次苹果新添加的附件通知，结合上通知拓展类，便可以给用户展现出一个有着丰富内容的通知。比如，一个小短片的某一秒的画面啊（这里要强烈鄙视各大平台的某些电影预览图），又或者是配上一些小图片啊（通过服务器传来的imaUrl）等方式来吸引用户，诱导用户点开你的通知，促使用户会使用你的App。其实推送这个功能，虽然有的人会关闭，但是大部分的人还是开启的，所以说推送这个市场还是很大的哟~灵活利用推送，会让你的程序拥有无限的可能。

### 1.2、如何新建一个UNNotificationServiceExtension

首先，我们不能通过创建UNNotificationServiceExtension的类来使用服务扩展，我们应当创建一个Target，这个Target自带一个模板，其中有2个方法是系统会自己调用的，如下：

```
//  你需要通过重写这个方法，来重写你的通知内容，也可以在这里下载附件内容
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *contentToDeliver))contentHandler;

//  如果处理时间太长，等不及处理了，就会把收到的apns直接展示出来
- (void)serviceExtensionTimeWillExpire;
```

开始跟着我创建一个UNNotificationServiceExtension吧。

### 新建Target

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/2.png>)

选择如图所示：

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/3.png>)

然后写名字，下一步，即可

此时我们的目录结构里面，已经多出了一个文件夹了。

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/4.png>)

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/5.png>)

都多了一个myTest。

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/6.jpg>)

注意看上图，这里的bundleID是你的工程名字的bundleID加上.名称。 不要修改，系统创建的时候就创建好了，不过我还是给大家说一下这个格式 如果你的工程的BundleID是coderxu.pushDemo,则这个扩展的BundleID就是coderxu.pushDemo.mytest,最后的后缀，是看咱们创建服务扩展时候的名字。其他的小细节，大家可以看看。 到这一步，我们就新建了一个服务通知类的扩展。

### 1.3、如何使用以及相关Demo

在使用这个类的时候，我重写了以下代码，大家可以先看下： (1). 这是处理通知内容重写的方法:

```
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    // copy发来的通知，开始做一些处理
    self.bestAttemptContent = [request.content mutableCopy];
    // Modify the notification content here...
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];

    // 重写一些东西
    self.bestAttemptContent.title = @"我是标题";
    self.bestAttemptContent.subtitle = @"我是子标题";
    self.bestAttemptContent.body = @"来自徐不同";

    // 附件
    NSDictionary *dict =  self.bestAttemptContent.userInfo;
    NSDictionary *notiDict = dict[@"aps"];
    NSString *imgUrl = [NSString stringWithFormat:@"%@",notiDict[@"imageAbsoluteString"]];

    if (!imgUrl.length) {
        self.contentHandler(self.bestAttemptContent);
    }

    [self loadAttachmentForUrlString:imgUrl withType:@"png" completionHandle:^(UNNotificationAttachment *attach) {

        if (attach) {
            self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];
        }
        self.contentHandler(self.bestAttemptContent);

    }];
}
```

(2). 这是下载附件通知的方法:

```
 - (void)loadAttachmentForUrlString:(NSString *)urlStr
                          withType:(NSString *)type
                 completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler{
    __block UNNotificationAttachment *attachment = nil;
    NSURL *attachmentURL = [NSURL URLWithString:urlStr];
    NSString *fileExt = [self fileExtensionForMediaType:type];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    [[session downloadTaskWithURL:attachmentURL
                completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
                    if (error != nil) {
                        NSLog(@"%@", error.localizedDescription);
                    } else {
                        NSFileManager *fileManager = [NSFileManager defaultManager];
                        NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];
                        [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

                        NSError *attachmentError = nil;
                        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];
                        if (attachmentError) {
                            NSLog(@"%@", attachmentError.localizedDescription);
                        }
                    }
                     completionHandler(attachment);
               }] resume];

}
```

(3)判断文件类型的方法

```
- (NSString *)fileExtensionForMediaType:(NSString *)type {
    NSString *ext = type;
    if ([type isEqualToString:@"image"]) {
        ext = @"jpg";
    }
    if ([type isEqualToString:@"video"]) {
        ext = @"mp4";
    }
    if ([type isEqualToString:@"audio"]) {
        ext = @"mp3";
    }
    return [@"." stringByAppendingString:ext];
}
```

第一段代码主要讲通知内容的重组，逻辑就是有附件的url，我就下载，如果没有url我就直接展示通知。 第二段代码主要讲的是，用系统自带类，下载图，存图，找到filePath，创建通知的附件内容。（创建附件的url，必须是一个文件路径，也就是说，必须下载下，才能获取文件路径,开头是file://） 第三段的代码主要讲判断文件的后缀类型，然后前端好处理。这里我的代码是写死了，因为我就测试一张图。最好的方式是服务器返回的 推送内容中，带有附件的类型。我的iOS开发 iOS10推送必看(高阶1)一文中，有讲多媒体附件的类型，以及相关的大小限制。

这里强烈建议，处理推送内容的时候，让服务器带上文件类型。重要的事情说三遍 这里强烈建议，处理推送内容的时候，让服务器带上文件类型。重要的事情说三遍 这里强烈建议，处理推送内容的时候，让服务器带上文件类型。重要的事情说三遍

在补充一些问题

1.在这个推送服务的扩展类中，为什么我使用了系统自带类下载，没有使用AFN？ 答：不知道为什么，导入AFN后总是出现各种编译报错啊。主要涉及这几个情况 1.用cocoapods导入AFN，在类引入AFN，编译报错！！ 2.手动拖拽AFN进工程（不勾选），在类引入AFN，编译报错！！ 3.手动拖拽AFN进工程(勾选)，直接编译报错！！ 4.下载用通知回调的方式（慢，线程不确定） 最后我就选择这个block回调，系统类下载的方式，来下载通知中的附件。 以下是一些错误的截图：(可以不看)

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/7.png>)

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/8.png>)

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/9.png>)

2.如果调试这个通知扩展类，为什么我跑程序的时候，打断点无反应？

答：这是因为你跑的target不对，正确的做法是，跑正确的target，具体如下图：

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/10.png>)

选择你的程序Target

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/11.png>)

这个时候，大家在打断点，就可以啦~

最后，在附上推送的内容格式。 推送内容格式如下：

```
{
    "aps": {
        "alert": "This is some fancy message.",
        "badge": 1,
        "sound": "default",
        "mutable-content": "1",
        "imageAbsoluteString": "http://upload.univs.cn/2012/0104/1325645511371.jpg"

    }
}
```

这里我们要注意一定要有”mutable-content”: “1”,以及一定要有Alert的字段，否则可能会拦截通知失败。（苹果文档说的）。除此之外，我们还可以添加自定义字段，比如，图片地址，图片类型，大家慢慢摸索下吧~有问题可以留言哟~

这一章的最后，附上成功推送的展示图:

![1](<https://allluckly.cn/images/blog/tuogao/tougao65/12.png>)

稍后补充以下内容~

```
# 2、UNNotificationContentExtension
## 2.1、UNNotificationContentExtension简介
## 2.2、如何新建一个UNNotificationContentExtension
## 2.3、如何使用以及相关Demo
# 3、UNNotificationAction
## 3.1、UNNotificationAction简介
## 3.2、如何新建一个UNNotificationAction
## 3.3、如何使用以及相关Demo
```

如果你喜欢我的文章，不要忘记关注我，谢谢大家了~ 另外如果你要转载，希望可以注明出处，我会写出更多更好的文章，来回馈大家~

重要的事情说三遍，[demo地址](<https://github.com/CoderWQ/XZPushTest/tree/master>) 重要的事情说三遍，[demo地址](<https://github.com/CoderWQ/XZPushTest/tree/master>) 重要的事情说三遍，[demo地址](<https://github.com/CoderWQ/XZPushTest/tree/master>)

---

好文推荐：[分分钟解决iOS开发中App启动动画广告的功能](<https://allluckly.cn/lblaunchimagead/LBLaunchImageAd>)

