今、Swift2 で書いている同胞達へ。最低限やっておきたい、Swift2 のままで始める Swift3 対策

Xcode8.2 を最後に Swift2 のサポートが終わりますね。Swift3 に移行祭りの季節が近づいてまいりました。

実は、Swift2 のコードを Swift3 に書き換え始めて 1 週間。まだお祭りしてます。

Xcode のマイグレーションツールは暴君です。
標準ライブラリだけでなく、<strong>自分たちで定義した</strong>変数名や列挙子などなど、問答無用で [API デザインガイドライン](https://swift.org/documentation/api-design-guidelines/)に則った形に変換します。
完全に置き換えしてくれるならまだしも、修正箇所が多いと変換が中途半端になります。
また、当然、RxSwift などのライブラリの変更部分の修正は自分でやらなくてはなりません。

でも、大丈夫です。今から戦う準備をしておけます。
Swift2 のうちから準備を進めておく利点としては下記が挙げられます。

* 動作確認しながら作業できます (Swift3 への変換を始めると置き換えが終わるまでコンパイルエラーになるので、動作確認できません)
* 修正箇所が少なくなるので、マイグレーションツールの精度がよくなります
* マイグレーションツールの変換による恐怖心が薄れます (マイグレーションツールはそこそこの頻度で誤った変換をするので、差分が多くなると怖いです…)
* 今から意識しつつ書いておけば、機能追加と並行して進められるので、結果的に工数が減ります。

Swift2 のときからやっておいてよかったこと + やっておけばよかったことの共有をしておきます。(いろいろあった気がするけれど、思い出したら追記します)

ちなみに、こちらは下記の環境です。

* Swift2.3 -> Swift3
* Xcode8.1
* Objective-C と混在
* Objective-C のライブラリを多数使用
* RxSwift 使用

# 参考
## とても助かった記事 (一部)
* [Xcode8+Swift3.0対応](http://qiita.com/noppefoxwolf/items/235eeec093afc3224a12)
* [各種ライブラリのSwift3での変更点](http://qiita.com/touyoubuntu/items/d40d2603aa066d48887e)
* [Swift2.2からSwift3.0への変換を行ってみて](https://samekard.blogspot.jp/2016/09/swift22swift30.html)
* [Swift2.2 から Swift3.0 への変換実例メモ](http://qiita.com/codelynx/items/54c96d69c3075450bb1e)

## Carthage の Swift 3 対応
Carthage も同様の方針で Swift3 対応を進めているとのことです！
しかも、具体的な作業がリスト化されていてとても参考になります！
([こちらのコメントで教えていただきました🙇]
(http://qiita.com/ezura/items/e7703eb04d2d1f2946b9#comment-112d1aa8502caab336ce))

* https://github.com/Carthage/Carthage/issues/1558#issuecomment-259115655
* https://github.com/Carthage/Carthage/projects/1

# Swift2 のうちからできること
## ネーミングをガイドラインに合わせる
今の段階から、できる限り[API デザインガイドライン](https://swift.org/documentation/api-design-guidelines/)に従っておいた方が良いと思います。
マイグレーションツールは、[API デザインガイドライン](https://swift.org/documentation/api-design-guidelines/)に則った形に半ば強引に変換します。
自分たちの定義した列挙子も、変数名も。

例えば、下記のような変換がされます。

* 列挙子が lower case に
* 真偽値型の変数のプレフィックスに `is` を付け加える

特に列挙子ですが、<strong>定義部分だけ lower case にして、case 文などで使用している箇所を合わせて修正してくれない</strong>ことが非常に多かったです。(やってくれる場合もありますが、体感的には半分くらいでした)
あと、RawValue を String にして、列挙子名をそのまま rawValue にしてる人は気を付けましょう。rawValue も列挙子名と一緒に変わりますよね…。

## legacy function の置き換え
`CGRectMake` や `CGRectGet[MaxX|MaxY|Width|Height]` など、Swift3 から廃止される関数の置き換えも Swift2 の時点ですでに可能です。
[こちらの記事](https://medium.com/swift-programming/swift-cgrect-cgsize-cgpoint-5f4196da9cf8#.u1ffwmtl0)にお世話になりました。
ちなみに、SwiftLint を走らせると警告が出て置き換えまで支援してくれるので大変便利でした。
(`view.frame.size.width` -> `view.frame.width` などの置き換えの支援もしてくれます)

## 標準ライブラリの型やプロトコルのリネームによる名前被りの回避
対策としては、ネームスペースを明示することです。

例えば、Swift3 で `ErrorType` が `Error` にリネームされました。
放置すると、下記のような悲劇が起きます。
<blockquote class="twitter-tweet" data-lang="en-gb"><p lang="ja" dir="ltr">Swift3 で &quot;〇〇Type&quot; の &quot;Type&quot; なくなって名前被りしてた(´・ω・`)<br>namespace で回避できるのが救い <a href="https://t.co/BCrDMu13OJ">https://t.co/BCrDMu13OJ</a> <a href="https://twitter.com/hashtag/swift?src=hash">#swift</a> <a href="https://t.co/FXgRa1oXyl">pic.twitter.com/FXgRa1oXyl</a></p>&mdash; ezura (@eduraaa) <a href="https://twitter.com/eduraaa/status/795958757662392320">8 November 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

Swift2 の段階で、ネームスペース (ここでは、`Swift.`) を明示しておけば解決しそうです。

```swift
struct API {
    enum Error: Swift.ErrorType {
        case hogeFailure
    }
}
```

## RxSwift の Swift3 バージョンへの対策
`onNext`, `onError`, `onComplete`, `subscribeNext` がなくなっています。
そのため、Swift3 で下記メソッドに書き換えていくことになります。
これらのメソッドは Swift2 版でも使えるので、今の内からこちらを使うようにしておくと後で楽でした。

```swift
func `do`(onNext: ((E) throws -> Void)? = nil, onError: ((Swift.Error) throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil, onSubscribe: (() -> ())? = nil, onDispose: (() -> ())? = nil)
        -> Observable<E> 
func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
        -> Disposable
```

```swift:例(Swift2)
Observable.of(hoge)
    .doOn(
        onNext: { /* do somethig */ },
        onError: { /* do somethig */ },
        onCompleted: { /* do somethig */  }
    )
    .subscribe(onNext: { /* do somethig */ })
    .addDisposableTo(disposeBag)

Observable.of(hoge)
    .doOn(
        onNext: { /* do somethig */ }
    )
    .subscribe(onNext: { /* do somethig */ })
    .addDisposableTo(disposeBag)
```

## Objective-C との連携周りの整備
じつはこれが一番厄介な問題かもしれません。
これを怠ると、「UILabel に `Optional("100") 円` なんて表示がされるようになっていた」というミスが発生しそうです…。

Objective-C で定義されている nullability アノテーションを指定していないメソッドやプロパティを Swift から呼ぶと、今までは IUO 型(`[型名]!`)で引数や戻り値が渡されていましたが、[こちらの変更](https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md)により Optional 型扱いとなります (IUO は型ではなく、属性として残ります)。
これによる問題の一つは、下記の動作の違いが発生することです。

```swift:Swift2
var v: String! = "a" // IUO 型
"\(v)"               // "a"
```

```swift:Swift3
var v: String! = "a" // IUO 属性
"\(v)"               // "Optional("a")"
```

[こちらの記事](http://qiita.com/WorldDownTown/items/29e8026fe76f4294bf8b#objective-c-%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%82%92%E6%96%87%E5%AD%97%E5%88%97%E3%81%AB%E7%B5%84%E3%81%BF%E8%BE%BC%E3%82%80%E3%81%A8%E3%81%8D%E3%81%AE%E4%B8%8D%E5%85%B7%E5%90%88)に詳しく載っています。

これによって必要となった変更は主に 3 つでした。これら全て、Swift2 の時点ではすでに修正可能だったのでやっておけば良かったです…。

* IUO 型で渡されて暗黙的に unwrap されていた値の unwrap 処理もしくは Optional chaining でのハンドリング
* block 等で引数の型を IUO 型で明示していた場合の対応
* nullability アノテーションの指定 (可能な限り)

### Swift2 と Swift3 での違い
```objc:Objective-C
@interface ParentInObjc : NSObject
- (NSString *)huga;
- (void)hogeWithBlock:(void (^)(NSString *param, NSError *error))block;
@end
```

```swift:Swift2
class ParentInObjc : NSObject {
    func huga() -> String!
    func hogeWithBlock(block: ((String!, NSError!) -> Void)!)
}
```

```swift:Swift3
class ParentInObjc : NSObject {
    func huga() -> String!
    func hoge(_ block: ((String?, Error?) -> Swift.Void)!)
}
```

```swift:呼び出し
#if swift(>=3.0)
    // Swift3
    let returnValue = ParentInObjc().huga()  // String?
    
    ParentInObjc().hoge { (text: String?, error: Error?) in
        // do something
    }
    
    ParentInObjc().hoge { (text, error) in
        // do something
    }
#else
    // Swift2
    let returnValue = ClassInObjc().huga()  // String!
    
    ClassInObjc().hogeWithBlock { (text: String!, error: NSError!) in
        // do something
    }
#endif
```

### Swift2 の段階から可能な対策
[nullability アノテーションの指定](https://developer.apple.com/swift/blog/?id=25)は可能な限りしておくと良いと思います。
ただ、難しい場合もありますよね。(外部のライブラリであったり…)
ということで、下記はそれ以外の対応策です。

#### Optional 前提でハンドリングを書く
```swift
// let returnValue = ParentInObjc().huga()  // String!
// to
let returnValue: String? = ParentInObjc().huga()

//: unwrap
if let value = returnValue {
    value.capitalizedString
}
// or
guard let value = returnValue else {
    assertionFailure("...")
    return
}
// or
returnValue?.capitalizedString
// or
returnValue!.capitalizedString
```

#### Block の引数を Optional として明示しておく
```swift
// こうしておく ⭕️
ParentInObjc().hogeWithBlock { (text: String?, error: NSError?) in
    // do something
}

// default 🙅
ParentInObjc().hogeWithBlock { (text: String!, error: NSError!) in
    // do something
}
```