# Xcode8开发iOS10推送通知过程

语言：zh-CN
原文：[Xcode8开发iOS10推送通知过程](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao60>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tougao60>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-09-21-adding-ios-10-push-notification-extensions-in-xcode-8/>)
发布于: 2016-09-21T00:00:00+08:00
内容更新于: 2016-09-21T00:00:00+08:00
署名：zhao0

记录在 Xcode 8 为现有应用接入 iOS 10 通知扩展的过程，集中讨论推送能力、签名、Swift 版本和架构设置，以及 Service、Content 扩展的调试与提交问题。

原文署名zhao0，Bison 编辑收录。

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

**阅读前** 这是 2015–2017 年间的技术记录，本次整理未重新运行验证示例。原文明确基于 Xcode 8、iOS 10，并讨论 Swift 2.3 与 Swift 3.0 的选择。

iOS10发布后，简书优先开发增加了iOS10的新通知。本文分享整个feature的开发过程遇到的问题。

编辑：[Bison](<https://allluckly.cn/>)  
 来源：[zhao0](<http://www.jianshu.com/p/133b535a4e90>)

## 1、工程配置

Xcode8发生了很大的变化，直接打开原来的工程编译运行，这个时候是获取不到Push token的，打印didFailToRegisterForRemoteNotificationsWithError中的error可以看到

```
fail to register with error Error Domain=NSCocoaErrorDomain Code=3000 
"no valid 'aps-environment' entitlement string found for application" 
UserInfo={
    NSLocalizedDescription=no valid 'aps-environment' 
    entitlement string found for application
}
```

Google了解到是Xcode8的变化，entitlements由本地的entitlements文件配置，主Target的Capabilities下，Push Notification处于关闭的状态，必须手动打开。

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

## 2、Code Sign

工程增加了Notification Content和Notification Service两个Extension，Signing默认是自动模式.。

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

据网上介绍Xcode8签名管理方式比Xcode7更智能，那我就试一下吧。选完Team它就自动生成了授权文件。然而，Xcode8还帮我生成了一个新的证书，相当于增加了一个开发人员，也就是我现在的证书不在原来的授权文件中，后果就是原来手动配置的授权文件要重新生成。

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

这时候如果想使用原始的手动配置，不仅要把Automatically manage signing去掉，还要在Key Chains里把新生成的证书删掉，然后在Web端生成授权文件。使用Automatically manage signing最好把每个Target都使用相同的方式。

## 3、Swift版本

如果你在创建Target的时候开发语言选了Swift，默认是使用Swift3.0，Xcode8同时支持3.0和2.3，可能你还没来及掌握3.0的API变化，想继续使用2.3的API，只要在对应Target的Build Setting中Use Legacy Swift Language Version设为YES就可以继续使用2.3。

## 4、Architectures

工程配好之后，只有模板代码，先跑起来写个Hello World再说。Command+R之后，编译报错了

```
Check dependencies
No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).
```

VALID\_ARCHS中没有对应的active arch

这时需要在对应Target的Build Setting中Valid Architectures中增加arm64

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

## 5、Notification Service

接下来，开始代码编写了，大部分是参考喵神的这篇文章活久见的重构 - iOS 10 UserNotifications 框架解析和Demo、还有WWDC 708 Advanced Notifications、PDF。 在service中，我们会把后端push过来的数据中的图片下载到本地文件夹，然后作为attachments。 之前我们的推送都只显示一句alert，不过推送的json数据alert字段可以是string，也可以是dictionary，如果是string，相当于dictionary中的body，在iOS8.2之后dictionary可以增加title、subtitle等。为了让推送更丰富，我们想把原来的body变成title，增加文章摘要放到body里，如果直接改aps内的alert，那么8.2以前的设备推送显示的是文章摘要了。后来我们想了一个办法，Notification Service不是可以修改推送内容吗？aps的alert还是保持不变，json增加一个新的title和body，Service收到通知之后把json中的title和body取出来赋给bestAttemptContent的title和body。

```
{
  "aps":{ 
        "alert":"test test",
        "mutable-content":1 
  }, 
  "title": "new title",
  "body": "new body"
}
```

## 6、Notification Content

如果通知有attachments，默认情况下长按通知会显示图片或视频，但是我们觉得显示那么大一个图片不美观，而且要有action button，就是下面这种效果，所以要用到Notification Content。 ![1](<https://allluckly.cn/images/blog/tuogao/tougao60/5.png>)

## 效果

其实这里没有自定义通知的UI，而是直接把这个View隐藏了。Info.plist中UNNotificationExtensionInitialContentSizeRatio表示view的初始height/width（其他属性参考WWDC），把它设为0.001（不能设为0），在viewDidload中设置self.preferredContentSize = CGSizeZero;来隐藏view。

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

这个Extension可以响应多个category，而每个category可以注册自己的action buttons，所以action buttons和customUI view是相互独立的，不过可以通过action buttons更新customUI view。 Action buttons默认的行为会打开APP，由delegate处理响应，使用了Notification Content后，我们可以在Notification Content直接处理响应而不用打开APP，也可以forward打开APP由delegate处理。

## 5、真机调试

代码写得差不多了，那就开始调试了。Extension调试过程中有时会遇到Could not attach to process ID 错误。

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

有时重启Xcode能解决，有时不能解决，这时候有一个方法，这个窗口弹出之后点OK，接着点Debug \> Attach to Process \> 你的Extension，这时候就能捕获断点了。

## 6、提交TestFlight

测试过了准备提交TestFlight，结果校验出错了。

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

错误信息很明显，Extension.appex中包含了Frameworks文件夹。如果Extension使用了Cocoapods，Cocoapods会创建这个文件夹，实际没什么用。解决办法就是在Build Phases最后增加一个Build Phase，执行一段shell脚本把Frameworks文件夹删掉。

```
cd "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"
if [[ -d "Frameworks" ]]; then
rm -fr Frameworks
fi
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao60/9.png>)  
 简书每个版本都会进行公测 公测详情，欢迎想要抢鲜的用户前往下载体验。

## 7、写在最后

简书作为一个优质原创内容社区，拥有大量优质原创内容，提供了极佳的阅读和书写体验，吸引了大量文字爱好者和程序员。简书技术团队在这里分享技术心得体会，是希望抛砖引玉，吸引更多的程序员大神来简书记录、分享、交流自己的心得体会。这个专题以后会不定期更新简书技术团队的文章，包括Android、iOS、前端、后端等等，欢迎大家关注。

---

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

