[Xcode] 自作frameworkを作成する


Xcodeで開発を行っていると必ず使う「****.framework」。
今回はこれの作成手順をまとめます。

ちなみにframeworkについてはドキュメントもあります。
[Framework Programming Guide: Introduction to Framework Programming Guide](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183-SW1)

## 実態はただのディレクトリ

実はframework自体はただのディレクトリです。
その中身の構造にフォーマットがあり、適切にファイルやディレクトリを配置することでXcodeがframeworkとして認識し、利用できるようになる、というわけです。

### ディレクトリ構造

[ドキュメント](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183-SW1)によると一番シンプルな状態は以下のようになります。

>#### A simple framework bundle
```
MyFramework.framework/
    MyFramework  -> Versions/Current/MyFramework
    Resources    -> Versions/Current/Resources
    Versions/
        A/
            MyFramework
            Resources/
                English.lproj/
                    InfoPlist.strings
                Info.plist
        Current  -> A
```

上記例では`Resources`しかありませんが、実際には`Headers`も含めることでヘッダファイルを個別にincludeする必要がなくなります。

実際に作成したキャプチャはこんな感じです↓
![cap.png](https://qiita-image-store.s3.amazonaws.com/0/5616/905273e8-ec56-cd8c-650a-19c95126ba06.png "cap.png")

よく見てもらうと分かりますが、エイリアスも生成されています。
階層直下にエイリアスを起き、実際はそれぞれ`Version`の下に入っているものが実態となります。
`Version`の名が示す通り、`A`以外にも`B`など複数バージョンを混在させることもできるようになっています。
（その場合は、`Current`の参照先を適切に変更することで利用するバージョンを切り替えることができるようになっています）

### 構造

構造は画像を見てもらうと分かると思いますが、フレームワーク直下には3つのエイリアスとひとつのディレクトリがあります。
`Headers`、`Hoge`、`Resources`はそれぞれ、`Versions/A`ディレクトリ以下のそれぞれ同名のものにリンクされています。
また、`Versions`の中に`Current`というエイリアスがあります。
このエイリアスは上記サンプルでは`A`を指しています。

しかし仮に、`B`などの別バージョンが存在する場合は、この`Current`の向き先を変えることでバージョン管理ができるようになっています。
（ちなみに拡張子なしの「Hoge」がライブラリ（.aファイル）の本体です）


### Run Scriptでファイルをコピー

ファイルが増えてきたら使えない（というかめんどくさい）ですが、以下のようにRun Scriptを設定して自分自身でディレクトリの生成やファイルのコピーを行うことで作成することが可能です。

サンプルではディレクトリの作成からファイルのコピー、そしてエイリアスの設定まですべてスクリプトで行っています。
普通（？）は`Headers`ディレクトリなどをプロジェクト内で作ってそこにファイルを入れておいたほうがいいのかもしれませんが、すでに作成してしまったプロジェクトをframework化する場合など、現状の構造では問題がある場合などはスクリプトでやっちゃうといいと思います。

```sh
#!/bin/bash

BUILD_DIRECTORY=build
FRAMEWORK_NAME=Hoge
FRAMEWORK_DIR=${BUILD_DIRECTORY}/${FRAMEWORK_NAME}.framework

rm -rf ${BUILD_DIRECTORY}/*

xcodebuild -scheme Hoge -configuration Debug -sdk $SDK_NAME
             
# ---------------------------------
# Create directories.
# ---------------------------------
FRAMEWORK_VERSION=A
mkdir -p ${FRAMEWORK_DIR}
mkdir -p ${FRAMEWORK_DIR}/Versions
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Resources
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Headers

# ---------------------------------
# Create aliases.
# ---------------------------------              
ln -s ${FRAMEWORK_VERSION} ${FRAMEWORK_DIR}/Versions/Current
ln -s Versions/Current/Headers ${FRAMEWORK_DIR}/Headers
ln -s Versions/Current/Resources ${FRAMEWORK_DIR}/Resources
ln -s Versions/Current/${FRAMEWORK_NAME}.a ${FRAMEWORK_DIR}/${FRAMEWORK_NAME}

# ---------------------------------
# Create an object file.
# ---------------------------------
lipo -create path/to/lib/libHoge.a \
-output ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/LobiRecMetal.a

# ---------------------------------
# Copy header files.
# ---------------------------------
cp ${PROJECT_DIR}/${PROJECT_NAME}/Hoge.h \
${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Headers/

# ---------------------------------
# Copy resouse files.
# ---------------------------------      
cp ${PROJECT_DIR}/${PROJECT_NAME}/sample.png ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Resources/
```

ポイントは`ln`コマンドでエイリアスを生成している部分です。
このあたりのルールはドキュメントに書いてある通りです。

上記を設定しておけば、ビルド実行時に必要なファイルがコピーされ、`Hoge.framework`が生成されます。
フレームワーク生成専用のビルドターゲットを作り、それをビルドすることでフレームワークが生成されるようにしておくといいと思います。

上記の構造が出来上がると、Xcodeにフレームワークとして追加した際に自動的に内容が読み込まれます。
あとは他のフレームワーク同様、ヘッダファイルをインポートして使うことができるようになります。


## 複数アーキテクチャのライブラリをひとつにまとめる`lipo`コマンド

### lipoコマンド

複数アーキテクチャ向けにビルドされたライブラリファイルをひとつにまとめ、ユニバーサルに対応させるためのコマンド。

以下のようにします。
（armv7とarm64を入れる、みたいな感じの例）

```console
lipo -arch armv7 ./path/to/armv7/libhoge.a -arch arm64 ./path/to/arm64/libhoge.a -create -output ./path/to/output/libhoge.a
```

今回の例ではひとつのアーキテクチャしか含めていませんが、複数アーキテクチャに対応する場合（というか、基本的には対応する必要があるでしょう）に使用します。


## frameworkで管理されているリソースにアクセスする

リソースにアクセスするには`NSBundle`を使います。
（frameworkをリンクさせると認識されます）

```objc
NSString *path = [NSBundle.mainBundle pathForResource:@"sample" ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
```

## 参考記事

- [Framework Programming Guide: Introduction to Framework Programming Guide](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183-SW1)
- [Xcode4.5.1でiOS用のframeworkを作成する](http://blog.excite.co.jp/spdev/18688449/)
- [[Mac][iOS] Frameworkとは？](http://cocoadays.blogspot.jp/2010/11/macios-framework.html#05)