Xcode の Release 時だけリソースをアプリ内に封入する方法

あまり需要がなさそうですが、ゲームとかの場合ですと大量の素材データをアプリ内に封入したりすることがあるじゃないかと思います。そしてそれらの素材を全部 Xcode に登録すると、デバッグ時でも転送するたびにその大量の素材データたちを転送する羽目になって非常に効率悪いです。もちろんオンデマンドリソースを使うのも一つの手ですが、ユーザにいちいち追加ダウンロードさせたくないし、こちらのデバッグも初回がサーバからダウンロードしないといけないとなると非常に不便なので、そこで取ったアプローチはリリースビルドだけリソースファイルをアプリ内に封入して、デバッグビルドはアプリ内に封入せず、iTunes でリソースをアプリの Documents フォルダーに入れる方法です。

というわけで、大まかに分けて 3 ステップです：

1. アプリを iTunes で書類転送をできるように設定する
2. リソースの取得用のクラスとか作って、Release ビルドと Debug ビルドではそれぞれ違うパスからリソースを取得させる
3. リソースを Release ビルドの時だけアプリに封入させる

## アプリを iTunes で書類転送をできるように設定する
まあこれは簡単ですね、Target の Info タブ行って `Application supports iTunes file sharing` の `Key` を追加して、`Type` を `Boolean` にして `Value` を `YES` にするだけです。そうするとアプリがデバイスにインストールされていれば、PC に接続して iTunes 開いてアプリのページ行くと街灯アプリの書類のやり取りができるようになります。詳しくは[こちらの記事](http://mushikago.com/i/?p=6835)などをご参照すればわかりやすいかと思います。
![スクリーンショット 2017-06-01 19.21.28（2）.png](https://qiita-image-store.s3.amazonaws.com/0/63998/b3ccaa18-6144-84db-871b-89bd241befe9.png)

## リソースの取得用のクラスとか作って、Release ビルドと Debug ビルドではそれぞれ違うパスからリソースを取得させる
Xcode は初期状態からデフォルトで Preprocessor Macros に `DEBUG=1` を入れてくれるので、Objective-C を使うのでしたら特に問題ないのですが、Swift はマクロではなく Swift Flag を使うので、Target の Build Settings ページに行って、Other Swift Flags という項目を探します（表示されなければ Build Settings の下のメニューバーの左側を Basic から All にすると出るはずです）。それの左に三角のボタンがあるので押すと `Debug` と `Release` がでてきます。ここで `Debug` の方に `-D DEBUG` のフラグを追加しておきましょう。そうすると Objective-C の時と同じように Debug と Release をコンパイル時にソースコードを分けることができます。
![スクリーンショット 2017-06-01 19.21.55（2）.png](https://qiita-image-store.s3.amazonaws.com/0/63998/b06359e2-5216-3e04-e3dd-531881b74a78.png)

そしてそのクラスからファイル名と拡張しで実際のリソースの URL を取得するので、DEBUG ならメインバンドル、それ以外なら Documents ディレクトリーという風に URL を返します。Swift ならこんな感じでいいでしょう

```swift
	func urlForResource(name: String, extension: String) -> URL? {
		#if DEBUG
			let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
			let resourceURL = documentDirectory?.appendingPathComponent(name).appendingPathExtension(`extension`)
			return resourceURL
		#else
			let mainBundle = Bundle.main
			let resourceURL = mainBundle.url(forResource: name, withExtension: `extension`)
			return resourceURL
		#endif
	}
```

Objective-C なら多分こんな感じでいけるはずかと思います

```objc
- (nullable NSURL*) urlForResourceWithName: (NSString*)name andExtension: (NSString*) extension {
#if defined DEBUG
	NSURL *documentDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
	NSURL *resourceURL = [[documentDirectory URLByAppendingPathComponent:name] URLByAppendingPathExtension:extension];
	return resourceURL;
#else
	NSBundle *mainBundle = [NSBundle mainBundle];
	NSURL *resourceURL = [mainBundle URLForResource:name withExtension:extension];
	return resourceURL;
#endif
}
```

## リソースを Release ビルドの時だけアプリに封入させる
さて一番肝心な部分です。これを実現するためにはまずリソースを Xcode に登録してはいけません（正確には登録しても Target Membership にチェックを入れてはいけません）。でないと問答無用でビルドするたびにリソースを転送します。

しかし Release ビルドには転送して欲しいので、ここで活躍するのがスクリプトです。1. の時とちょっと似てますが、今回は Target の Build Phase の方に行って、その直下のメニューバーの左に `+` があるのでそれをクリックします。そしたら色々追加できますがここで `New Run Script Phase` を選びます。

選んだらスクリプトが追加されますが、ここで下記のようなスクリプトを追加します

```bash
if [ "$CONFIGURATION" != "Debug" ]; then
    cp -R ../Data/. ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/
fi
```

簡単に説明すると、要するに Scheme の Configuration が `Debug` じゃなければ（つまり Release 設定であれば）、`cp -R ../Data/. ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/` を実行するということです。ここで `cp -R` は指定のディレクトリ内のものを構造そのまま別のディレクトリーにコピーするということです。`../Data/.` というのはコピー元はプロジェクトフォルダーの一個上の `Data` フォルダー内（つまり Data フォルダーと Project フォルダーは同列にある）のすべてのファイルということです。これはみなさんの実際状況に合わせて変えましょう。`${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/` というのはコピー先はビルド先のアプリバンドル内ということです。これは多分変更することはないかと思われます。
![スクリーンショット 2017-06-01 19.24.36（2）.png](https://qiita-image-store.s3.amazonaws.com/0/63998/a864b1d8-11ea-5ed6-4ee2-7f9842a72846.png)

以上。