おつかれさまです。藤澤です。
今回は開発が捗る(かもしれない)ちょっとした小ネタをご紹介します。
みなさんは Xcode で開発をしていて「リソースファイルを置き換えたのに実行したら古いままだった」ということはないでしょうか。最近ぼくのまわりで しばしばこの現象起きています。そんなときは一度プロジェクトを Clean したら解決するのですが、ソースが増えてプロジェクトが大きくなってくると Clean Build に時間がかかるようになり、リソースを置き換えるたびに かなりの時間待たされることになります。これでは かなわないので、今回はこの現象についてちょっと調べてみました。
そもそも Xcode でプロジェクトを Build したとき、リソースを更新するかどうかの判定は以下のように行なわれているようです。
- Build の成果物である実行ファイル(app ファイル)は DerivedData フォルダ以下(※)に作成される。
- リソースファイルは app ファイル内に格納(コピー)される。
- Resources フォルダと app ファイル内のファイルを比べて、Resources フォルダのほうが新しい場合 app ファイル内のリソースファイルを更新する。
- それがフォルダである場合、そのフォルダ内のファイルに対して 3.を繰り返す。
※ デフォルトでは ~/Library/Developer/Xcode/DerivedData/{プロジェクト名}/Build/Products/{Debug-iphoneos とか}/{プロジェクト名}.app
おおかた予想どおりの挙動ですが、4.が曲者です。
Mac でファイルを上書きコピーしたとき そのフォルダのタイムスタンプも更新されますが、親フォルダのタイムスタンプは更新されません。したがって Resources フォルダから二階層以上深いサブフォルダのリソースを変更しても、Resources フォルダから見たタイムスタンプが更新されていないため実行ファイルに反映されないわけです。
具体的には、以下の構成で a.png を変更すると image フォルダのタイムスタンプが新しくなるためリソースが更新されますが、b.png を変更しても image フォルダのタイムスタンプが変わらないため変更なしとみなされ、リソースは更新されません。
ではファイルを確実に更新するにはどうすればよいでしょうか。
残念ながら常にリソースを更新するようなオプションは見つけられませんでした。
考えられる対応としてはこんなものがあると思います。
- プロジェクトにフォルダを追加する際、「Create folder references for any added folders」オプションを使わずに「Create groups for any added folders」を使う
- リソースファイルを変更したら その親フォルダの日付も最新化する
- app ファイル内のリソースファイルを直接置き換える
1.は最初に設定しておけば あとは作業は必要ありませんが、「Create groups for any added folders」オプションにすると app ファイル内にコピーされるときフォルダ階層を維持しないようなので、場合によっては問題になります。(同名のファイルが複数あるとか)
2.と3.はリソースを変更するたびに作業が必要になりますが、それほど手間ではありませんし、頻繁にリソースを変更するようであればスクリプトを書いてもよいかと思います。
2.の場合は touch コマンドでフォルダのタイムスタンプを更新できます。カレントフォルダ以下のすべてのサブフォルダのタイムスタンプを更新するのであればこんな感じです。
#!/bin/sh find . -type d -print0 | xargs -0 touch
3.は単純にコピーすれば OK です。app ファイルに対しても
cp a.png hoge.app/image/a.png
みたいな感じでコピーできます。当然ながら app ファイルだけ変更して Resources フォルダに入れ忘れると Clean Build とかのタイミングで元に戻ってしまいますのでご注意を。
今回は以上です。知ってる人には当たり前の内容だと思いますが、同じように困っている方がいましたら今回の記事がお役に立てば幸いです。