MUSHIKAGO APPS MEMO

[Xcode6] SwiftでCocoa Touch Frameworkを作る

いろいろなアプリで共有する機能をFrameworkとして自作しようと思い、Xcode6のSwiftでどうやって作るかを試してみました。まだ作りこむ前なので、今後もいろいろと問題が出てきそうですが、その準備段階までを簡単にスクリーンショット付きでMEMOしておきます。

※ このMEMOは、有料iOSデベロッパー向けに公開されているベータ版「Xcode 6.2 beta 4」を使っています。Xcode6.1.xではこの通りに動かないかもしれません。

ややこしいのは、作ったFrameworkが実機用とiOSシミュレーター用で別のものになるという点ですね。
2つの異なるフレームワークファイルを合体させて最終的なものを仕上げる必要がありそうですが(追記:「[Xcode6] Universal Frameworkを作る」を書きました。)、ここではそこまでは触れず、まずは、iOSシミュレーターで動作を確認しながらFrameworkを作るという準備段階までを忘れないうちにMEMOしときます。

また、Xcode6からは、ひとつのiOSアプリを作りながら、同じプロジェクト内でその一部としてFrameworkを作っていくようなスタイルが取れそうなのですが(例えば、今後のアプリで共有する機能をFrameworkにする想定で、最初の一つ目のアプリと一緒にそのFrameworkを作っちゃうような感じ?)、ここでは、Frameworkを単体のプロジェクトとして作り、それを、また別のiOSアプリプロジェクトでリンクして利用する形で作るようにしてみます。(Frameworkのそのものだけをひとつのレポジトリとして管理したかったので)

Cocoa Touch Frameworkを作る

Xcodeを起動し、File > New > Projectから「Cocoa Touch Framework」を選択。

「JiminyCricket」という名前のFrameworkにしています。その他もろもろを入力後、Languageを「Swift」に。その後、そのプロジェクトを適当な場所に保存。

「JiminyCricket」というフォルダの中に新たにクラスファイルを作ります。コンテキストメニューなどから「New File」を選択。

今度は、「Cocoa Touch Class」を選択。

クラス名も「JiminyCricket」にしちゃいます。「NSObject」のサブクラスとしておきましょう。Languageは「Swift」で。

保存ダイアログはそのままでいいと思います。こんな感じ。

まだビルドしていないので、Productsの中の「JiminyCricket.framework」などが存在しておらず赤文字です。先ほど作成した「JiminyCricket.swift」にデフォルトで書き込まれている「class JiminyCricket: NSObject」をpublicにして、中にtest()メソッドを作っておきましょう。これもpublicに。これが呼ばれたら「JiminyCricket test() is called.」と出力するようにしておきます。こんな感じになります。

まず、ターゲットを「iOS Device」にしてビルドします。これは実機用のFrameworkを書き出していることになります。

そして、この辺大事なのですが、「iOS Simulator」用にもビルドします。「iOS Simulator」のどれか(「iPhone6」とか)を選択してビルド。

作成されたFrameworkたちがどこにあるのかを確認してみましょう。「JiminyCricket.framework」を選択して、「Show in Finder」。

「Debug-iphoneos」というフォルダにあるものが実機用で、「Debug-iphonesimulator」というフォルダに入っているものがiOSシミュレーター用のFrameworkファイルということです。

これで一応、Frameworkを作成するためのプロジェクトが準備できました。

自作したFrameworkを使ってみる

今度は使う側のアプリを作っていきます。File > New > Projectを選択。

「Single View Application」を選びましょう。

今度は、「WoodenHead」という名前のアプリにしてみます。Languageは「Swift」で。

で、ここでどのFrameworkファイルを読み込むべきなのか結構悩みました。しばらくはiOSシミュレーターで確認しながら作りたいので、とりあえずは、iOSシミュレーターで動作するものをリンクしておきます。
(これは、まずその形で作っていき、ある程度仕上がったら、自作Frameworkの「JiminyCricket.framework」を最終的な形にして、その時点で読み込み直す感じになるのかな、と思っています)

先ほど、「Show in Finder」にて開いた「Debug-iphonesimulator」フォルダの中の「JiminyCricket.framework」ファイル(こちらがiOSシミュレータ用)をドラッグして、使う側のiOSアプリ「WoodenHead」のプロジェクトの方にドロップします。位置は、一番最上位の場所でいいと思います。

ファイルを追加する際のオプションダイアログが出ますが、「Copy items if needed」はチェックせずにそのまま参照した形でリンクしておきましょう。

ちょっと、読み込んだ中を見てみます。「JiminyCricket-Swift.h」なるファイルが自動的に作られており、メソッドなどがしっかり定義されているようです。

SingleView Applicationの最初のビューとして開かれる「ViewController.swift」を開き、「import JiminyCricket」と書きましょう。自動的に認識してくれないので手書きで書き込む感じになりますね。

「import JiminyCricket」を追加後は、そのFrameworkを認識し、「JiminyCricket」と書こうとするとコードヒントが出てくれます。まずは、これがちゃんと出るかですね。この時注意すべき点は、ターゲットが「iPhone Simulator」のどれか(例えば「iPhone6」)になっているかというところです。リンクしているFrameworkがiOS Simulatorのみで動作するものになっており、コードを書く時点でもターゲットをちゃんと選んでおく必要があるようです。「iOS Device」となっている場合は、コードヒントがちゃんと出てくれません。ここも結構ひっかかりました。

メソッド「test()」もちゃんと認識してくれますね。

ターゲットに注意して、Runボタンを押してみましょう。

で、次に引っかかるのがコレです。そのままだとうまく動かないんですね。ちゃんとiOS Simulator用にしていても「dyld: Library not loaded」「Reason: image not found」というエラーが出てしまいます。

これはいくつか解決法がありそうなのですが、とりあえずiOSシミュレータで作っている間は埋め込んでしまおう的な方法があるようです。Generarlの下の方に「Embedded Binaries」というところがあるので、そこに「JiminyCricket.framework」をドラッグアンドドロップして追加します。すると「Linked Frameworks and Libraries」にもう一つリストされてしまうため、これは「-」を押して削除しておきます。

再度、Runボタンを押すと、先ほどのエラーは出なくなり、JiminyCricket.framework内のtest()メソッドが呼ばれたことが確認できます。

frameworkを更新してみる

では、JiminyCricket.frameworkの続きを作っていけるのかを確認してみます。
自作Frameworkの「JiminyCricket」プロジェクトに戻り、新しくhello()メソッドを追加してみます。ターゲットがiOS Simulator向けになっているかを確認した上で、Runボタンを押してビルド。

再度、Frameworkを使っている側のiOSアプリ「WoodenHead」プロジェクトに戻りコードを書いてみると、ちゃんとリンクしていたFrameworkの機能も更新されており、hello()のコードヒントも出るようになっています。

Runボタンを押してiOSシミュレータで確認してみると、hello()メソッドも呼び出せたことがわかります。

とりあえず、ここまで準備できたので、しばらくこの形でフレームワークとそれを使っているアプリを同時進行で作ってみます。そのうち、フレームワークを実機とiOSシミュレータ用の両方で使えるようにしたり(追記:「[Xcode6] Universal Frameworkを作る」を書きましたので参考に。)と、やらねばならないことも出てくると思いますので、そうなったら、またその時点でMEMOを追加するようにしたいと思います。

[追記]:つづき

→「[Xcode6] Swiftの自作FrameworkにGoogle Analytics SDKを組み込んでみる

→「[Xcode6] 自作Frameworkを実機で動くようにしてみる

→「[Xcode6] Universal Frameworkを作る