[iOS]カスタムドキュメントのサムネイルを表示する - Thumbnail Extension

こんにちは。
@gachalatteです。

今回は、カスタムドキュメントのサムネイルを表示する`Thumbnail Extension`を紹介します。`Thumbnail Extension`の実装は難しいものではありませんが、インターネットで検索してもほとんど情報が得られない状況ですので、ここにまとめておきます。

[前回の記事](https://qiita.com/gachalatte/items/b5a7839cb53eb34415d2)とあわせてご覧ください。


# プロジェクトにThumbnail Extensionを追加する

1. Xcodeのメニューから、`File` > `New` > `Target...`を選択します。
2. `Application Extension` > `Thumbnail Extension`を選択します。
3. `Product Name`に`ThumbnailExtension（任意）`を入力します。

![プロジェクトを追加](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553312/93b4d187-2e39-a703-63f7-4ac436079889.png)


# Info.plistにカスタムドキュメントを登録する

1. `DocumentBaseApp` > `Targets` > `ThumbnailExtension` > `Info`を開きます。
2. `NSExtension` > `NSExtensionAttributes` > `QLSupportedContentTypes`にカスタムドキュメントのUTIを追加します。

![Info.plist](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553312/0ede161e-1a26-0071-ce82-d781429e95f5.png)


# ThumbnailProviderを実装する

`ThumbnailExtension` > `ThumbnailProvider.swift`を開いて実装します。`ThumbnailProvider`の実装方法が3種類あることが示されていますので、最適な方法を選択してください。


```swift:ThumbnailProvider.swift
class ThumbnailProvider: QLThumbnailProvider {
    
    override func provideThumbnail(for request: QLFileThumbnailRequest, _ handler: @escaping (QLThumbnailReply?, Error?) -> Void) {
        
        // There are three ways to provide a thumbnail through a QLThumbnailReply. Only one of them should be used.
        
        // First way: Draw the thumbnail into the current context, set up with UIKit's coordinate system.
        handler(QLThumbnailReply(contextSize: request.maximumSize, currentContextDrawing: { () -> Bool in
            // Draw the thumbnail here.
            
            // Return true if the thumbnail was successfully drawn inside this block.
            return true
        }), nil)
        
        /*
        
        // Second way: Draw the thumbnail into a context passed to your block, set up with Core Graphics's coordinate system.
        handler(QLThumbnailReply(contextSize: request.maximumSize, drawing: { (context) -> Bool in
            // Draw the thumbnail here.
         
            // Return true if the thumbnail was successfully drawn inside this block.
            return true
        }), nil)
         
        // Third way: Set an image file URL.
        handler(QLThumbnailReply(imageFileURL: Bundle.main.url(forResource: "fileThumbnail", withExtension: "jpg")!), nil)
        
        */
    }
}
```

今回は、ドキュメント内の`image.png`をそのまま返すように実装してみます。（実際は、`QLFileThumbnailRequest`の要求に従った画像を返すようにしてください。）

```swift:ThumbnailProvider.swift
class ThumbnailProvider: QLThumbnailProvider {
    
    override func provideThumbnail(for request: QLFileThumbnailRequest, _ handler: @escaping (QLThumbnailReply?, Error?) -> Void) {
        let imageFileURL = request.fileURL.appendingPathComponent("image.png")
        handler(QLThumbnailReply(imageFileURL: imageFileURL), nil)
    }
}
```

# デバッグする

ターゲットを`ThumbnailExtension`にして実行するとデバッグすることができます。起動するAppを尋ねられたら`DocumentBaseApp`を選択します。この挙動は、`Product` > `Scheme` > `Edit Scheme...` > `Run` > `Info` > `Executable`で変更することができます。


# 完成

`ThumbnailExtension`の実装が完成したら、ターゲットを`DocumentBasedApp`に戻します。

`UIDocumentBrowserViewController`にサムネイルが表示されるようになりました。`ファイル.app`でもサムネイルが表示されます。

![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553312/07a9dc5a-298d-bbec-85b2-92bb68d5928d.jpeg)

# 参考

* [Providing Thumbnails of Your Custom File Types](https://developer.apple.com/documentation/quicklookthumbnailing/providing_thumbnails_of_your_custom_file_types)
* [Building Great Document-based Apps in iOS 11](https://developer.apple.com/videos/play/wwdc2017/229/)