iOS10發布后,簡書優先開發增加了iOS10的新通知。本文分享整個feature的開發過程遇到的問題。
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處於關閉的狀態,必須手動打開。

2、Code Sign
工程增加了Notification Content和Notification Service兩個Extension,Signing默認是自動模式.。

據網上介紹Xcode8簽名管理方式比Xcode7更智能,那我就試一下吧。選完Team它就自動生成了授權文件。然而,Xcode8還幫我生成了一個新的證書,相當於增加了一個開發人員,也就是我現在的證書不在原來的授權文件中,后果就是原來手動配置的授權文件要重新生成。

這時候如果想使用原始的手動配置,不僅要把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 dependenciesNo 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

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。

其實這里沒有自定義通知的UI,而是直接把這個View隱藏了。Info.plist中UNNotificationExtensionInitialContentSizeRatio表示view的初始height/width(其他屬性參考WWDC),把它設為0.001(不能設為0),在viewDidload中設置self.preferredContentSize = CGSizeZero;來隱藏view。

這個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錯誤。

有時重啟Xcode能解決,有時不能解決,這時候有一個方法,這個窗口彈出之后點OK,接着點Debug > Attach to Process > 你的Extension,這時候就能捕獲斷點了。
6、提交TestFlight
測試過了准備提交TestFlight,結果校驗出錯了。

錯誤信息很明顯,Extension.appex中包含了Frameworks文件夾。如果Extension使用了Cocoapods,Cocoapods會創建這個文件夾,實際沒什么用。解決辦法就是在Build Phases最后增加一個Build Phase,執行一段shell腳本把Frameworks文件夾刪掉。
cd "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"if [[ -d "Frameworks" ]]; thenrm -fr Frameworksfi

