LoginSignup
9
12

More than 5 years have passed since last update.

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

Posted at

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

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

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

アプリを iTunes で書類転送をできるように設定する

まあこれは簡単ですね、Target の Info タブ行って Application supports iTunes file sharingKey を追加して、TypeBoolean にして ValueYES にするだけです。そうするとアプリがデバイスにインストールされていれば、PC に接続して iTunes 開いてアプリのページ行くと街灯アプリの書類のやり取りができるようになります。詳しくはこちらの記事などをご参照すればわかりやすいかと思います。
スクリーンショット 2017-06-01 19.21.28(2).png

リソースの取得用のクラスとか作って、Release ビルドと Debug ビルドではそれぞれ違うパスからリソースを取得させる

Xcode は初期状態からデフォルトで Preprocessor Macros に DEBUG=1 を入れてくれるので、Objective-C を使うのでしたら特に問題ないのですが、Swift はマクロではなく Swift Flag を使うので、Target の Build Settings ページに行って、Other Swift Flags という項目を探します(表示されなければ Build Settings の下のメニューバーの左側を Basic から All にすると出るはずです)。それの左に三角のボタンがあるので押すと DebugRelease がでてきます。ここで Debug の方に -D DEBUG のフラグを追加しておきましょう。そうすると Objective-C の時と同じように Debug と Release をコンパイル時にソースコードを分けることができます。
スクリーンショット 2017-06-01 19.21.55(2).png

そしてそのクラスからファイル名と拡張しで実際のリソースの URL を取得するので、DEBUG ならメインバンドル、それ以外なら Documents ディレクトリーという風に URL を返します。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 なら多分こんな感じでいけるはずかと思います

- (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 を選びます。

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

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

以上。

9
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12