iOS11のスクリーンショットをApp Extensionで使う方法


この記事はSwift その2 Advent Calendar 2017 18日目のコンテンツです。

App Extensionとは

iOS8からある機能
四角から上に矢印伸びたボタンを出てくるアレ

App Extensionの基本的な使い方

if let extensionContext = self.extensionContext,
   let item = extensionContext.inputItems.first as? NSExtensionItem,
   let attachments = item.attachments as? [NSItemProvider],
   let attachment  = attachments.first {
   let contentType = kUTTypeImage as String

   attachment.loadFileRepresentation(forTypeIdentifier: contentType) { (url, error) in
   // Do Something with url
   // url -> Image,  url -> Data
  })            
}

loadFileRepresentation()というメソッドを使って画像URLを取得
していました。URLあれば、なんでもできそうだったからです。URLからUIImageに変換したり、URLからDataに変換したり。

iOS10以前と11でのURLの違い。

if let extensionContext = self.extensionContext,
   let item = extensionContext.inputItems.first as? NSExtensionItem,
   let attachments = item.attachments as? [NSItemProvider],
   let attachment  = attachments.first {
   let contentType = kUTTypeImage as String

   attachment.loadFileRepresentation(forTypeIdentifier: contentType) { (url, error) in
   // Do Something with url
   // iOS10
   // “file:///private/var/mobile/Containers/**/P105.PNG”
   // 
   // iOS11
   // “file:///private/var/mobile/Containers/**/image”
  })            
}

しかし、Screenshotを取った時にApp Extensionで取得できるURLが上記のように末尾が /image なってしまいました。このURLをもとにUIImageやDataに変換しようとしてもエラーが出てしまいます。

そこで以下のように対処しました。

if let extensionContext = self.extensionContext,
   let item = extensionContext.inputItems.first as? NSExtensionItem,
   let attachments = item.attachments as? [NSItemProvider],
   let attachment  = attachments.first {
   let contentType = kUTTypeImage as String

   attachment.loadItem(forTypeIdentifier: contentType, options: nil) { (data, error) in
       if type(of: data!) == UIImage.self {
          // iOS 11 screenshot
       } else if type(of: data!) == NSURL.self {

       }
   }            
}

この方法にはいくつか欠点があります。

  • 想定していないタイプが来たらどうする?
  • ドキュメントは?
-> 観測範囲ではない

最後に紹介した方法で動くは動くのですが、他になにかいい方法あったら教えてください。