[iOS 8] App Extension #2 – Embedded Framework を利用して共有コードを Framework 化する

containing app と App Extension の共通コード

App Extension は containing app が提供する機能の一部を外部から利用できるようにする仕組みです。このため、当然ながら containing app と Extension の間には共通で利用したいコードやリソースが発生することになります。そのような場合に便利なのが Embedded Framework です。

Embedded Framework

Embedded Framework はアプリにバンドルされた状態の Framework です。Framework を利用するアプリのビルド時に、コンパイル後のアプリのバイナリや Storyboard などの他のリソースと同様にアプリ内にパッケージングされます。

以前は iOS 用 Framework を作成しようとすると非常に面倒でした。しかし、Xcode 6 からは iOS 用 Framework を作成するためのテンプレートが用意されているため、とても簡単に作成することができます。

Embedded Framework の作成手順

実際に Embedded Framework を作成する手順を説明していきます。あらかじめ、Extension のターゲットが追加してあるアプリのプロジェクトを Xcode で作成しておいて下さい。

ターゲットの作成

まず、Embedded Framework のターゲットを追加します。Project navigator でプロジェクトを選択すると、中央ペインにプロジェクトの設定画面が表示されます。General タブを選択して左下の + ボタンをクリックして下さい。

ios-embeddedframework_001

テンプレート選択画面が表示されますので、iOS > Framework & Library の中から Cocoa Touch Framework を選択して下さい。

ios-embeddedframework_002

ターゲットの設定画面が表示されたら、Product Name を入力して Finish をクリックします。

ios-embeddedframework_003

Project navigator に Framework のフォルダが、プロジェクト設定画面の TARGETS に Framework のターゲットが追加されているのが確認できるかと思います。

ios-embeddedframework_004

App Extension 用の設定

App Extension には、iOS SDK で提供されている API のうち、NS_EXTENSION_UNAVAILABLE マクロが付いているものに関しては利用することができないという制約が存在します。このマクロが付いている API を Extension で利用していると、App Store に申請した際にリジェクトされるという記述が Apple の公式ドキュメントに存在します。このため、Extension が利用する Framework についても同様に、NS_EXTENSION_UNAVAILABLE マクロが付いている API を利用しないようにしなくてはなりません。

幸い、Framework のターゲットには Extension で利用できない API を利用した場合にコンパイルエラーを発生させるオプションがあります。Extension から利用する Framework を作成する際には、必ずそのオプションを有効化しておきましょう。

先程作成した Framework のターゲットを選択すると、ターゲットの設定画面が表示されます。この中の Deployment Info > App Extensions に "Allow app extension API Only" という項目がありますので、これにチェックを入れます。

ios-embeddedframework_005

これで、Extension で利用できない API を Framework 中で利用しようとした場合に、コンパイラが指摘してくれます。

ios-embeddedframework_012

Framework のリンク

作成した Framework を containing app と Extension から利用できるようリンクの設定を行います。

アプリへのリンク

まずは、アプリ本体のターゲットの設定を確認して下さい。先程作成した Framework が既にリンク設定済みになっていますので、特に設定を行う必要ありません。さらに、Embedded Binaries にも追加されており、アプリ本体をビルドした際にこの Framework が Embed されるよう既に設定されています。

ios-embeddedframework_006

Extension へのリンク

続いて、Extension のターゲットの設定を確認して下さい。こちらは、Framework のリンク設定がされていませんので設定を追加する必要があります。

ios-embeddedframework_007

ターゲット設定画面に Linked Frameworks and Libiraries セクションがあります。その左下の + ボタンをクリックしてください。

ios-embeddedframework_013

Framework および Library の選択画面が表示されますので、先程作成した Framework を選択して Add ボタンをクリックします。

ios-embeddedframework_008

これで Extension にも Framework がリンクされました。

ios-embeddedframework_009

公開クラスの設定

Framework に実装した共通モジュールを Embedded Framework をリンクしたアプリから利用できるよう、公開するヘッダの設定を行います。

Headers の設定

Framework のターゲットを選択して、Build Phases タブを選択して下さい。この中に Headers というセクションがあり、Framework に追加されたコードのヘッダが登録されています。ここでは仮に、ImageService というクラスを公開したいものとします。

ios-embeddedframework_010

Framework にクラスを追加すると、Headers セクション内の Project という項目に登録されます。これを Public に移動します。ドラッグ&ドロップで操作することができます。

ios-embeddedframework_011

Umbrella Header の設定

Embedded Framework を作成した際に、ターゲットと同名のヘッダファイルが作成されます。このヘッダファイルは Umbrella Header と言い、Framework が公開するクラスのヘッダの import をまとめて宣言します。このヘッダファイルに共通モジュールクラスのヘッダを import します。

#import <UIKit/UIKit.h>

//! Project version number for ActionExtensionNoUIEmbeddedLib.
FOUNDATION_EXPORT double ActionExtensionNoUIEmbeddedLibVersionNumber;

//! Project version string for ActionExtensionNoUIEmbeddedLib.
FOUNDATION_EXPORT const unsigned char ActionExtensionNoUIEmbeddedLibVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <ActionExtensionNoUIEmbeddedLib/PublicHeader.h>
#import <ActionExtensionNoUIEmbeddedLib/ImageService.h>

これで Framework に実装したクラスを containing app や Extension から利用できるようになりました。

Framework の利用

作成した Framework のクラスを containing app や Extension のコードから利用するには、下記のように @import による import を宣言するだけです。

@import ActionExtensionNoUIEmbeddedLib;

まとめ

Embedded Framework を利用すれば、containing app と Extension のコードやリソースの共有がとても簡単に実現できます。Extension を実装する際には積極的に利用したいですね。