iOSでUSDZファイルのAR表示（AR Quick Look）

# はじめに

[WWDC2018](https://developer.apple.com/videos/wwdc2018/)で発表されたusdz形式のファイルでのAR表示（AR Quick Look）についてまとめます。

 - USDZとは
 - 対応機種
 - Quick Look表示のイメージ
 - Webコンテンツ内にUSDZファイルを配置する方法
 - ネイティブアプリでAR表示する場合の方法
   - WKWebViewでの表示は未対応 
   - SFSafariViewControllerで表示する場合
   - QLPreviewControllerで表示する場合
 - サンプルリポジトリ

## usdzとは

 - 3Dデータ用のファイル形式
 - ピクサーが2013に発表したUSD形式の改良版
 - iOS12以降の対応機種であればブラウザやネイティブアプリでQuick Look表示が可能

<img src="https://qiita-image-store.s3.amazonaws.com/0/113553/58708866-e07b-5644-b66b-e13c3fc4e98c.png" width="200px">

## 対応機種

iOSでの対応機種は以下のようになっています。

 - iPhone 6s以降
 - すべてのiPad Proモデル
 - iPad（第5世代）
 - iPad（第6世代）

※OSはiOS12以降

さらにWWDCの発表では以下のメモリ容量の大きいデバイスが推奨されていました。

 - iPhone 7 Plus
 - iPhone 8 Plus
 - iPhone X
 - iPad Pro 12.9

## Quick Look表示のイメージ

iO12の対応機種であればSafariで[AR Quick Lookギャラリー](https://developer.apple.com/jp/arkit/gallery/)を見ることで、Quick Look表示を体験できます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/113553/13d3842d-35e5-4e89-1b6c-25f9b22af284.png" width="500px">

 - ARコンテンツを含むWebページを表示した場合、上記のようにAR対応のアイコン（写真左）が画像の上に表示される
 - オブジェクトタブでは白い何もない空間上に3Dモデルが表示（写真中央）
 - ARタブでは背面カメラを利用して平面を検知した時に3Dモデルが配置される（写真右）

試してみた感想としては、**なかなか平面が検知されないのでカメラでいろんな方向を撮る手間**がある、3Dモデルの**サイズが大きめに表示されてしまう**などのiOS側の課題がありそうだなと感じました。

## Webコンテンツ内にUSDZファイルを配置する方法

WebコンテンツでUSDZファイルを配置するにはaタグのhref属性にusdzファイルのパスを、rel属性に"ar"を設定します。内部にimgタグを配置すると自動でAR対応のアイコンが表示されます。この設定をしない場合、ブラウザがusdzという拡張子を判定することができません。

```html
<a href="item.usdz" rel="ar" >
    <img src="item_thumbnail.png">
</a>
```

以下のようにWebサーバーのMIME設定も必要なようです。

```
AddType model/vnd.pixar.usd .usdz
AddType model/usd .usdz
```

## ネイティブアプリでAR表示する場合の方法

### WKWebViewでの表示は未対応
WKWebViewでの表示も試みましたが、表示できませんでした。
以下のWebKitのサイトにも書かれていますが、**WKWebViewでの表示は未対応**のようです。

[Viewing Augmented Reality Assets in Safari for iOS](https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/)
> Other than Safari, the AR integration is available in SFSafariViewController clients. We’ve also received requests to add it to WKWebView.

標準ブラウザ（Safari）を利用せず、ネイティブアプリで表示する方法は以下の2つです。

 - SFSafariViewController
 - QLPreviewController

### SFSafariViewControllerで表示する場合

以下のようにURLを指定してあげればOKです。ただし、SFSafariViewControllerはカスタマイズできることがかなり制限されているので、実際に利用する場合は次のQLPreviewControllerを使うことが多いかと思います。

```swift
import SafariServices
import UIKit

public final class SafariViewController: SFSafariViewController {
    let url: URL
    
    init(url: URL) {
        self.url = url
        super.init(url: url, configuration: Configuration())
    }
}
```

### QLPreviewControllerで表示する場合

以下のようにQLPreviewControllerDataSourceを実装すればOKです。

```swift
import UIKit
import QuickLook

public final class ARNativeViewController: QLPreviewController {
    let url: URL
    
    init(url: URL) {
        self.url = url
        super.init(nibName: nil, bundle: nil)
        dataSource = self
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension ARNativeViewController: QLPreviewControllerDataSource {
    public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return 1
    }
    
    public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        // URL must be a file-type URL (local files only)
        return url as QLPreviewItem
    }
}
```

注意点として、**指定するURLは端末内に保存されたusdzファイルのURLである必要**があります。そのため、WebサーバーからUSDZファイルを取得する場合は一度ローカルディレクトリにファイルを保存する必要があります。以下はAlamofireを利用してusdzファイルをダウンロードするサンプルコードです。

```swift
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendingPathComponent("teapot.usdz")
    
    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

let url = URL(string: "https://developer.apple.com/arkit/gallery/models/teapot/teapot.usdz")!
Alamofire.download(url.absoluteString, to: destination)
    .responseData { [weak self] response in
        DispatchQueue.main.async {
            self?.openInNative(url: url)
        }
}
```

## サンプルリポジトリ
サンプルコードはこちらのリポジトリに置いてあります。

[github.com/shtnkgm/ARQuickLook](https://github.com/shtnkgm/ARQuickLook)

<img src="https://qiita-image-store.s3.amazonaws.com/0/113553/0a34d61c-1151-e6c5-ed84-000338c2f1f5.png" width="400px">

# まとめ
 - USDZとQuickLookを利用することで非常に簡単に3DモデルのAR表示ができる
 - USDZファイルをネイティブで表示するにはWKWebViewは未対応なので注意
 - QLPreviewControllerを利用することが多いと想定される
 - QLPreviewControllerを利用する場合にもWeb上のUSDZファイルは一度端末にダウンロードが必要

# 参考サイト

本記事を作成するにあたり参考にさせていただいたサイトです。

 - [Apple、ARKit 2を発表](https://www.apple.com/jp/newsroom/2018/06/apple-unveils-arkit-2/)
 - [Apple Engine - ARKit で使用されている USDZ とは何か](http://appleengine.hatenablog.com/entry/2018/06/18/160521)
 - [速報：アップル、AR向け新3DフォーマットUSDZ発表。iOS 12のARKit 2で導入](https://japanese.engadget.com/2018/06/04/ar-3d-usdz-ios-12-arkit-2/)
 - [Qiita - 【iOS 12】 AR Quick Look機能で自作モデルをSafariから見る](https://qiita.com/shiruco/items/b248f267b47a8ef797b1)
 - [Qiita - SFSafariViewController はトラッキングの夢を見るか](https://qiita.com/akitaika_/items/8955a6f3ff5a8447f9e7)
 - [wwdc2018 - Integrating Apps and Content with AR Quick Look](https://developer.apple.com/videos/play/wwdc2018/603/)
 - [wwdc2018 - Quick Look Previews from the Ground Up](https://developer.apple.com/videos/play/wwdc2018/237/)