Xcodeで開発を行っていると必ず使う「****.framework」。
今回はこれの作成手順をまとめます。
ちなみにframeworkについてはドキュメントもあります。
Framework Programming Guide: Introduction to Framework Programming Guide
実態はただのディレクトリ
実はframework自体はただのディレクトリです。
その中身の構造にフォーマットがあり、適切にファイルやディレクトリを配置することでXcodeがframeworkとして認識し、利用できるようになる、というわけです。
ディレクトリ構造
ドキュメントによると一番シンプルな状態は以下のようになります。
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する必要がなくなります。
よく見てもらうと分かりますが、エイリアスも生成されています。
階層直下にエイリアスを起き、実際はそれぞれVersion
の下に入っているものが実態となります。
Version
の名が示す通り、A
以外にもB
など複数バージョンを混在させることもできるようになっています。
(その場合は、Current
の参照先を適切に変更することで利用するバージョンを切り替えることができるようになっています)
構造
構造は画像を見てもらうと分かると思いますが、フレームワーク直下には3つのエイリアスとひとつのディレクトリがあります。
Headers
、Hoge
、Resources
はそれぞれ、Versions/A
ディレクトリ以下のそれぞれ同名のものにリンクされています。
また、Versions
の中にCurrent
というエイリアスがあります。
このエイリアスは上記サンプルではA
を指しています。
しかし仮に、B
などの別バージョンが存在する場合は、このCurrent
の向き先を変えることでバージョン管理ができるようになっています。
(ちなみに拡張子なしの「Hoge」がライブラリ(.aファイル)の本体です)
Run Scriptでファイルをコピー
ファイルが増えてきたら使えない(というかめんどくさい)ですが、以下のようにRun Scriptを設定して自分自身でディレクトリの生成やファイルのコピーを行うことで作成することが可能です。
サンプルではディレクトリの作成からファイルのコピー、そしてエイリアスの設定まですべてスクリプトで行っています。
普通(?)はHeaders
ディレクトリなどをプロジェクト内で作ってそこにファイルを入れておいたほうがいいのかもしれませんが、すでに作成してしまったプロジェクトをframework化する場合など、現状の構造では問題がある場合などはスクリプトでやっちゃうといいと思います。
#!/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を入れる、みたいな感じの例)
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をリンクさせると認識されます)
NSString *path = [NSBundle.mainBundle pathForResource:@"sample" ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];