前置き
最近のオープンソースなSwift製ライブラリは、Embedded Frameworkとして使うことを前提としているものが多いです。それらをiOS 7対象のプロジェクトで使う場合にはソースコードを直接取り込むしか方法がないのですが、他のライブラリに依存しているライブラリは、コード中にimport DependentLibrary
やDependentLibrary.SomeClass
などと出てくるので、そのままでは使うことができません。
「RxSwiftをiOS 7で使う」のようにプロジェクトに直接取り込む方法を用意しているライブラリはそれを使います。そうでないライブラリの場合はソースコードの改変が必要になります。しかし、import
だけならソースコードを改変せずにごまかすことができるんじゃないかと思いました。
要するに、例えばimport DependentLibrary
と書かれていたら、ダミーのDependentLibrary.framework
を作ってあげればよいのです。そして、そのFrameworkは「Static Framework」としてビルドして、iOS 7でも使えるようにします。実際のソースコードは直接取り込みつつ、同じ名前のFrameworkを用意することで、import
をごまかすわけです。
例:APIKitを改変せずにiOS 7で使う
というわけで、例としてResultに依存しているSwift製ライブラリであるAPIKitを、ソースコードの改変なしにiOS 7で動かしてみます。
1. APIKitとResultのソースコードをプロジェクトに追加する
APIKitとResultをチェックアウトして、含まれているソースコードをすべて自身のプロジェクトに追加します。ただし、APIKitのFormURLEncodedDataParser.swiftだけはiOS 8以上でないとビルドできないので外します。
2. Bridging HeaderにAPIKit.hを追加する
APIKitはワークアラウンドのために一部Objective-Cで書かれているので、Bridging HeaderにAPIKit.h
を追加します。前回を参考にしてください。
3. Resultという名前のターゲットを追加する
ここが肝です。APIKitのソースコード中のimport Result
をごまかすために、Result
という名前のStatic Frameworkを作ります。
昔はStatic Frameworkを作るには、まずStatic Libraryを作って、他のファイルも作って、フォルダ構成を整えて、と面倒だったのですが、いまはテンプレートが用意されているので簡単にできます。
まず、プロジェクトに新しいターゲットを追加して、テンプレートから「Cocoa Touch Framework」を選びます。名前をResult
に、言語を「Objective-C」にします。Embedded Frameworkとしては使わないので、「Embed in Application」は「None」にしておきます。
このままではDynamic Frameworkになってしまうので、「Linking > Mach-O Type」を「Dynamic Library」から「Static Library」にします。iOS 7で使うので「Deployment Target」も7.0
にします。
4. ダミーのResult.frameworkをターゲットに追加します。
APIKitを使いたいターゲットに、今回作ったダミーのResult.framework
を含めるようにします。Dynamic Frameworkではないので「Linked Frameworks and Libraries」のほうに追加します。
5. APIKitを使ったコードを書いてiOS 7で動かしてみる
ここまでやるとビルドも通るようになり、iOS 7対象のプロジェクトでもAPIKitが使えるようになっているはずです。Demo.playgroundのGitHub APIの例などで試してみてください。
サンプルプロジェクト https://github.com/bricklife/APIKit-iOS7
ダミーのFrameworkでは解決しないケース
中身が何も入っていないFrameworkなので、Frameworkの名前空間や一部のインポートには対応できません。
例えば、ReactiveCocoaの以下の部分などが、Frameworkの名前空間に依存しています。public typealias Observer = Observer<Value, Error>
とするとビルドできないので、どちらかの名前を変えざるを得ないと思います。
public typealias Observer = ReactiveCocoa.Observer<Value, Error>
また、同じくReactiveCocoaの以下の部分などで、Frameworkの一部をimport
しているのですが、これも対応できません。
import enum Result.NoError
まとめ
手段が目的と化している感じがしますが、ライブラリのソースコードを改変しなければアップデートに追従する手間が格段に違うので、まだiOS 7をターゲットにしているプロジェクトを担当している方はぜひ試してみてください!