自作ライブラリをXcode8・Swift2.3/3.0対応する際にハマったこと

# まえおき

ふたつほど、マイクロライブラリを公開しています。

Notifwift: NSNotificationCenter wrapper for Swift
https://github.com/takasek/Notifwift

ActionClosurable: Extensions which helps to convert objc-style target/action to swifty closures
https://github.com/takasek/ActionClosurable

これらをSwift2.3/3.0に対応させていった手順を、記録に残しておきます。

# どう対応するか

## branch

ふたつ用意しましょう。

- swift3.0用のbranch(masterでもいい)
- swift2.3用のbranch

ユーザーがどちらのbranch(タグ)も選べるようにしておかないといけないので、必ず並行でbranchを稼働させるようにしましょう。


## コーディング

Xcodeで自動的にmigrationできて、コンパイルが通ったからそれでOK…
だなんて、思ってませんか？

### Migration Guideを読もう

[Swift.org - Migrating to Swift 2.3 or Swift 3 from Swift 2.2](https://swift.org/migration-guide/)

公式のドキュメントをしっかり読んで、適切な実装について理解しましょう。

Notifwiftでは、Notification名をこれまでString型で受けていたAPIを、 `Notification.Name` で受けるように変更しました。
後方互換性のためにStringのままにすることもできたのですが、あえて破壊的変更を選びました。
なぜなら、ガイドに次のような記述があったからです。

>In Swift 3, many of Foundation’s “stringly-typed” APIs have been changed to use struct “wrapper types”, such as the new Notification.Name type. Since, it’s common for notification names and other string constants to be declared globally or as static members, the best way to take advantage of these new types is usually to construct the wrapper at the point of declaration:
>
```swift
static let MyGreatNotification = Notification.Name("MyGreatNotification")
// Use site (no change)
NotificationCenter.default().post(name: MyController.MyGreatNotification, object: self)'
```

### アクセス修飾子もちゃんとしよう

これも、Xcodeの機械的なmigrationに任せるだけでなく、ちゃんと手を入れたい箇所。
Xcodeのmigrationに任せると、 `public` だった修飾子が `open` に変更されていまいます。
しかし、継承させたい意図がない場合はopenではなくpublicを使うべき。
同じく、その型の中でだけアクセスできればいいメンバについては、 `fileprivate` ではなく `private` を使いましょう。


### ネーミングルールにこだわろう

Xcodeのmigrationでは、標準のAPIはmigrateできても、ライブラリのAPIはそのままです。
これをSwift3らしくリネームするかどうかは、あなた次第。
でも、かっこいいAPIにしたほうがいいんじゃないかなー。

[Swift 3 その基本ルールを眺める #cswift](http://www.slideshare.net/tomohirokumagai54/swift-3-cswift)
32ページ以降を読んでおくといいと思います
あるいはmono0926さんの
[Swift API Design Guidelinesの紹介(Swift 3版) - Qiita](http://qiita.com/mono0926/items/11203c8cce0d7f4548db)


## パッケージ管理対応

### Carthage

何も罠がありませんでした。
素晴らしい。

### CocoaPods

つらかったです。

#### CocoaPodsのバージョンは、 `1.1.0 rc2` を使う

コマンドラインで案内が出てたので、深く考えずに以下を実行したのだけれど…
それだけだと使えませんでした。

```
CocoaPods 1.1.0.rc.2 is available.
To update use: `sudo gem install cocoapods --pre`
```

既にインストールされていたリリース版が優先されてしまったのです。

```
[Notifwift]@23:09 % pod --version
1.0.1
```

どうやればRC版を優先できるのか悩んだ結果…
`"Quick and Dirty"` なやり方に従うことにしました。

[How to use different CocoaPods versions · Bitrise](http://devcenter.bitrise.io/docs/cocoapods-how-to)

```
[Notifwift]@23:41 % gem uninstall --all --executables cocoapods
ERROR:  While executing gem ... (Gem::InstallError)
    cocoapods is not installed in GEM_HOME, try:
	gem uninstall -i /Users/tksk_mba/.rvm/gems/ruby-2.2.1@global cocoapods
[Notifwift]@23:42 % gem uninstall -i /Users/tksk_mba/.rvm/gems/ruby-2.2.1@global cocoapods

Select gem to uninstall:
 1. cocoapods-1.0.1
 2. cocoapods-1.1.0.rc.2
 3. All versions
> 1
Successfully uninstalled cocoapods-1.0.1
[Notifwift]@23:42 % pod --version
1.1.0.rc.2
```

(2016/10/5追記)
@kishikawakatsumiさんからコメントいただきました。
http://qiita.com/takasek/items/c42641ca1ca03bb2ee39#comment-951c31b3b3b8d49ca985

>CocoaPods（に限らずRubyGems一般）のバージョンを使い分けるのはBundlerを使うのが便利です。

詳しい使用法はコメントをご参照ください。



#### `.swift-version` を作る

CocoaPodsのtrunkは、そのライブラリのバージョンを判別する際に `.swift-version` というファイルの内容を参照するようです。

https://github.com/CocoaPods/CocoaPods/issues/5864#issuecomment-247106640

これはechoコマンドを使えば、手っ取り早く作れます。

```
echo "2.3" > .swift-version
```

あるいは

```
echo "3.0" > .swift-version
```

#### あとはいつもの手順

1. 最新のbranchにtagつけて、
2. `pod lib lint` して、
3. `pod trunk push`

```
[Notifwift]@0:10 % pod lib lint                                                           (git)-[master]

 -> Notifwift (1.0.0)

Notifwift passed validation.
[Notifwift]@0:14 % pod trunk push                                                         (git)-[master]

[!] Found podspec `Notifwift.podspec`
Updating spec repo `master`
Validating podspec
 -> Notifwift (1.0.0)

Updating spec repo `master`
  - Data URL: https://raw.githubusercontent.com/CocoaPods/Specs/65608e550b0c28061345a403759ce7cd7a812863/Specs/Notifwift/1.0.0/Notifwift.podspec.json
  - Log messages:
    - September 23rd, 00:15: Push for `Notifwift 1.0.0' initiated.
    - September 23rd, 00:15: Push for `Notifwift 1.0.0' has been pushed (0.387305114 s).
[Notifwift]@0:16 %
```

### Swift Package Manager

そんなに難しくないらしい？
余裕があれば。
自分は余裕なくなったので、またの機会にやります…。

# すべて済んだら、

## READMEをしっかり書こう

Swift2.3用branch、Swift3.0用branch、それぞれのREADMEに、最適化された形で次の情報があると親切です。

- Swift2.2バージョンからのAPI変更点のまとめ、あるいはmigrationの手順
- Cartfileに記述すべき内容
- Podfileに記述すべき内容
- Swift2.3用branchであれば、Swift3.0用branchへのリンク
- Swift3.0用branchであれば、Swift2.3用branchへのリンク

以上であなたのライブラリのSwift2.3/3.0対応は完了です。
お疲れ様でした。