このブログのコピーです。
概要
iOS10からリッチ通知機能のひとつとしてメディア添付ができるようになりました。
そこで実際に動画や音声を動的(main bundleに入れずに通知時にダウンロードする方法)でやってみたところ思わぬ制限がありました。
iOS開発では当たり前なのかもしれませんが、ドキュメントにも見つけられなかったのでまとめました。
環境
- macOS Sierra 10.12.3
- Xcode 8.2.1
- iOS 10
やったこと
カスタムペイロード(キーは決め打ち)にメディアのURLを入れてリモートPushし、通知を受信したタイミングでダウンロード、再生までをやりました。
コード
実は意外とめんどい。
まずはNotification Service Extensionを作成するためにProvisioning Profileを作成します。(アプリ側とは別に)
作成したらXcodeのメニュー[File]-[New]-[Target]から[Notification Service Extension]を選択し[Next]、new targetのなんやかんやを設定し、[Finish](さっきのProvisioning Profileとか)
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
let IMAGE_URL_KEY = "image_url"
let MOVIE_URL_KEY = "movie_url"
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let imageUrl: String = request.content.userInfo[self.MOVIE_URL_KEY] as! String? {
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: {[weak self](data, response, error) in
do {
if let writePath = NSURL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent("tmp.mp4") {
try data?.write(to: writePath)
let identifier = "XXXX"
if let bestAttemptContent = self?.bestAttemptContent {
let attachment = try UNNotificationAttachment(identifier: identifier, url: writePath, options: nil)
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
return
}
} else {
if let bestAttemptContent = self?.bestAttemptContent {
bestAttemptContent.title = "\(bestAttemptContent.title) [URL不正]"
contentHandler(bestAttemptContent)
}
}
} catch let error as NSError {
NSLog(error.localizedDescription)
if let bestAttemptContent = self?.bestAttemptContent {
bestAttemptContent.title = "\(bestAttemptContent.title) [ダウンロード失敗]"
contentHandler(bestAttemptContent)
}
}
})
task.resume()
} else {
if let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "\(bestAttemptContent.title) [image_url取得失敗]"
contentHandler(bestAttemptContent)
}
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
結果
mutable-contentを1、カスタムペイロードとして、movie_urlにmp4ファイルを指定してPush通知すると動画が再生できます… 音声が無ければ
確認してみたところ…
- ダウンロードした音なし動画→再生可能
- ダウンロードした音あり動画→ 再生不可
- ダウンロードした音声→ 再生不可
- main bundleに置いた音なし動画→再生可能
- main bundleに置いた音あり動画→再生可能
- main bundleに置いた音声→再生可能
となりました。
Soundペイロードの件があったので、もしかすると音声はLibrary/Sounds配下に置けばダウンロードでも再生できるのかな…と思ったけど試してません。
ハマったこと・課題など
- エラーが起きても通知処理自体は何食わぬ顔で処理されるので最初、動画形式が悪いのかとか全然ちがうところを調べてたorz
- エラーがExtensionが異常終了みたいな出方しかしないし。
github
なし