SwiftのライブラリをiOS 7で使うためにダミーのFrameworkを作ってimportをごまかす

# 前置き

最近のオープンソースなSwift製ライブラリは、Embedded Frameworkとして使うことを前提としているものが多いです。それらをiOS 7対象のプロジェクトで使う場合にはソースコードを直接取り込むしか方法がないのですが、他のライブラリに依存しているライブラリは、コード中に`import DependentLibrary`や`DependentLibrary.SomeClass`などと出てくるので、そのままでは使うことができません。

「[RxSwiftをiOS 7で使う](http://qiita.com/bricklife/items/54fb324296b09425b1a8)」のようにプロジェクトに直接取り込む方法を用意しているライブラリはそれを使います。そうでないライブラリの場合はソースコードの改変が必要になります。しかし、`import`だけならソースコードを改変せずにごまかすことができるんじゃないかと思いました。

要するに、例えば`import DependentLibrary`と書かれていたら、ダミーの`DependentLibrary.framework`を作ってあげればよいのです。そして、そのFrameworkは「Static Framework」としてビルドして、iOS 7でも使えるようにします。実際のソースコードは直接取り込みつつ、同じ名前のFrameworkを用意することで、`import`をごまかすわけです。

# 例：APIKitを改変せずにiOS 7で使う

というわけで、例として[Result](https://github.com/antitypical/Result)に依存しているSwift製ライブラリである[APIKit](https://github.com/ishkawa/APIKit)を、ソースコードの改変なしにiOS 7で動かしてみます。

## 1. APIKitとResultのソースコードをプロジェクトに追加する

[APIKit](https://github.com/ishkawa/APIKit)と[Result](https://github.com/antitypical/Result)をチェックアウトして、含まれているソースコードをすべて自身のプロジェクトに追加します。ただし、APIKitの[FormURLEncodedDataParser.swift](https://github.com/ishkawa/APIKit/blob/master/Sources/DataParserType/FormURLEncodedDataParser.swift)だけはiOS 8以上でないとビルドできないので外します。

## 2. Bridging HeaderにAPIKit.hを追加する

APIKitはワークアラウンドのために一部Objective-Cで書かれているので、Bridging Headerに`APIKit.h`を追加します。[前回](http://qiita.com/bricklife/items/54fb324296b09425b1a8#3-rxcocoahをbridging-headerで読み込む)を参考にしてください。

## 3. Resultという名前のターゲットを追加する

ここが肝です。APIKitのソースコード中の`import Result`をごまかすために、`Result`という名前のStatic Frameworkを作ります。

昔はStatic Frameworkを作るには、まずStatic Libraryを作って、他のファイルも作って、フォルダ構成を整えて、と面倒だったのですが、いまはテンプレートが用意されているので簡単にできます。

まず、プロジェクトに新しいターゲットを追加して、テンプレートから「Cocoa Touch Framework」を選びます。名前を`Result`に、言語を「Objective-C」にします。Embedded Frameworkとしては使わないので、「Embed in Application」は「None」にしておきます。

<img width="740" alt="Screen Shot 2016-06-03 at 16.36.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/31516/33448a0d-aec5-b220-3b44-97a4e2faaf7d.png">

<img width="736" alt="Screen Shot 2016-06-03 at 16.37.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/31516/c5ae5d01-9636-432a-abd5-9bca37147674.png">

このままではDynamic Frameworkになってしまうので、「Linking > Mach-O Type」を「Dynamic Library」から「Static Library」にします。iOS 7で使うので「Deployment Target」も`7.0`にします。

<img width="567" alt="Screen Shot 2016-06-03 at 16.37.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/31516/218dc379-040f-f61d-747e-cac5b128809f.png">

## 4. ダミーのResult.frameworkをターゲットに追加します。

APIKitを使いたいターゲットに、今回作ったダミーの`Result.framework`を含めるようにします。Dynamic Frameworkではないので「Linked Frameworks and Libraries」のほうに追加します。

<img width="416" alt="Screen Shot 2016-06-03 at 16.50.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/31516/039c2c1b-d42d-334b-0077-461bbca95f99.png">

## 5. APIKitを使ったコードを書いてiOS 7で動かしてみる

ここまでやるとビルドも通るようになり、iOS 7対象のプロジェクトでもAPIKitが使えるようになっているはずです。[Demo.playground](https://github.com/ishkawa/APIKit/tree/master/Demo.playground)のGitHub APIの例などで試してみてください。

サンプルプロジェクト https://github.com/bricklife/APIKit-iOS7

# ダミーのFrameworkでは解決しないケース

中身が何も入っていないFrameworkなので、Frameworkの名前空間や一部のインポートには対応できません。

例えば、ReactiveCocoaの以下の部分などが、Frameworkの名前空間に依存しています。`public typealias Observer = Observer<Value, Error>`とするとビルドできないので、どちらかの名前を変えざるを得ないと思います。

```swift:ReactiveCocoa/Swift/Signal.swift
	public typealias Observer = ReactiveCocoa.Observer<Value, Error>
```

また、同じくReactiveCocoaの以下の部分などで、Frameworkの一部を`import`しているのですが、これも対応できません。

```swift:ReactiveCocoa/Swift/FoundationExtensions.swift
import enum Result.NoError
```

# まとめ

手段が目的と化している感じがしますが、ライブラリのソースコードを改変しなければアップデートに追従する手間が格段に違うので、まだiOS 7をターゲットにしているプロジェクトを担当している方はぜひ試してみてください！