iOSでstaticライブラリを開発していると画像等を同梱したい場合がある。
通常だとaファイルの他にbundleを作成する必要があるが、
これをaファイルの中に同梱する方法を調べた。

やり方としては、画像などのバイナリデータを一旦16進数情報に変換、
ライブラリのソース内にchar配列として保持し、
それを実行時にNSDataで読み込みUIImageに変換する感じになる。

バイナリデータを16進数に変換する

xxdコマンドでバイナリデータを16進数に変換できる。
例えばあるpngファイルをxxdコマンドにかけると以下のような出力になる。

$ xxd original.png

00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 02ee 0000 0536 0802 0000 0045 b5a4  .......6.....E..
00000020: 5100 0000 0173 5247 4200 aece 1ce9 0000  Q....sRGB.......
00000030: 4000 4944 4154 7801 ecbd 0794 1dc7 79ef  @.IDATx.......y.
00000040: 79f3 e4c1 2067 2232 8101 cc20 0966 5282  y... g"2... .fR.
00000050: a8b0 b244 59d2 932c 796d e959 b2fd deca  ...DY..,ym.Y....
00000060: a215 8eed b59f c33b 4eeb f5f3 8ab6 f73c  .......;N......<
00000070: 9f77 1cf6 59b6 ec5d db92 455a 6612 730e  .w..Y..]..EZf.s.
00000080: 2041 82c8 3963 1027 cfdc b4bf 3b05 140b   A..9c.'....;...
00000090: dd7d efdc 7bfb cef4 0cf0 ef83 33a8 aeae  .}..{.......3...

さらに、pオプションをつけるとプレーンな16進数出力になる。

$ xxd -p original.png

89504e470d0a1a0a0000000d49484452000002ee00000536080200000045
b5a451000000017352474200aece1ce900004000494441547801ecbd0794
1dc779ef79f3e4c1206722328101cc2009665282a8b0b24459d2932c796d
e959b2fddecaa2158eedb59fc33b4eebf5f38ab6f73c9f771cf659b6ec5d
db92455a6612730e204182c839631027cfdcb4bf3b05140bdd7defdc7bfb
cef40cf0ef8333a8aeaefaaaead7b7bbfefd557575fc81071e88c7e3b132
5bb15834474863c3c4d8b027be8c9980e80a8506a46e5c94ad79e34cca92
08888008888008348c4054fd63c31a50bb218f9630bb2e8742a19048245c
c36e6f9e62c7ee938ed46e660e99ddd1542559e31e65d7e6750ba81cf658
a89cb8e147a32dbde1cd914111100111100111983c042aab82725db02797

逆に、16進数出力結果をバイナリ形式に変換することもできる。
その場合はrオプションをつける。

xxd original.png > original.hex
xxd -r original.hex > revert.png

オプション無しの出力結果の他に、-pの出力結果もrevertできる。
その場合はpオプションを付与する。

xxd -r -p original.hex > revert.png

画像データをCのchar配列形式で出力

ここからが本題で、xxdコマンドにiオプションを付与すると以下のように画像データをCのchar配列形式で出力できる。

$ xxd -i original.png

unsigned char original_png[] = {
  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x02, 0xee, 0x00, 0x00, 0x05, 0x36,
 // 中略
  0x80, 0x10, 0x10, 0x02, 0x42, 0x20, 0x4f, 0x08, 0xfc, 0xff, 0x2c, 0x24,
  0xa4, 0xb5, 0x8f, 0x74, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
  0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int original_png_len = 954870;

これをNSDataに食わせて、それをUIImageとして扱うことでソースから画像を生成することができる。

+ (UIImage *)revertHexImage
{
  unsigned char original_png[] = {
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
   // 中略
    0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
  };
  unsigned int original_png_len = 954870;

  // 画像データからNSDataを生成し、それを元にUIImageを生成する
  NSData *data = [NSData dataWithBytes:original_png
                                length:original_png_len];
  return [UIImage imageWithData:data];
}

今回は説明簡単にするためにメソッドの中にベタ書きしたけど、
実際には別ファイルに書き出してincludeするとかしたほうが良さそう。