この記事は ピクシブ株式会社 Advent Calendar 2016、24日目の記事です。
こんにちは、 @FromAtom です。pixiv SketchとpixivコミックのiOSアプリ開発を担当しています。
さて、世間はクリスマスだなんだと浮かれていますが、我々エンジニアにとっては年末調整とSwift3対応の季節です。みなさんはSwift3ですか? 今回はpixivコミックアプリのSwift3対応をした際のあれこれについて、書いていきたいと思います。
Swift3対応を始める前に
Swift3対応を開始する日を決めておきましょう。Swift3対応のブランチが切られてからは、メインのブランチには致命的なクラッシュ対応以外はマージしないほうが良いです。既存のコードをSwift3化するだけでも大変なのに、新しくマージされたコードもSwift3化していくと、無限に対応が終わらず担当者が疲弊します。
つまり、Swift3対応をしている間は新機能追加やバグフィックスができなくなります。ユーザに価値が届けられない時間が結構長く続くので、「なぜSwift3対応をしないといけないのか?」をチームメンバーにしっかりと共有し、いつからコードをフリーズしても良いかをしっかりと調整しておくことが大切です。
人数
1人でやりました。Swift3にconvertすると大量のエラーが出るのですが、Xcodeが途中でビルドを諦めてしまうため「ファイル毎に担当者を決めてエラーを潰していく」ということが難しいです。複数人で手分けしてやるのは厳しいので、担当者を1人決めてえいやっと終わらせるか、ペアプロでやると良いと思います。
日数
僕がやった時には2週間かかりました。利用しているライブラリの数やコードの規模にもよりますが、テスト期間を含めて2〜3週間程度を見ておくと良いでしょう。
ライブラリ
僕がSwift3対応を始めた9月に比べて、いまでは多くのライブラリがSwift3対応をしているかと思います。現時点でSwift3対応していないライブラリは、メンテがされていないライブラリなので、使うのをやめましょう。他のライブラリを探すか、自分で作ってしまいましょう。
なお、CocoaPodsやCarthageは最新版を利用しておけば大丈夫です。Carthageは場合によっては --no-use-binaries
が必要になりますのでご注意ください。
その他細かいハマりどころ
NSがなくなった
NSURL
や NSNotification
などから NS
のprefixがなくなりました。詳しくは下記リンクにまとまっています。
- swift-evolution/0069-swift-mutability-for-foundation.md at master · apple/swift-evolution · GitHub
- swift-evolution/0086-drop-foundation-ns.md at master · apple/swift-evolution · GitHub
これらは、Xcodeのconvert機能で変換されます。しかし、自分で URL
や Notification
という名前のクラスや構造体を作っている場合、名前がかち合ってしまいます。この場合 Foundation の URL
や Notification
は Foundation.URL
と Foundation.Notification
として変換されます。
正しく動作はするのですが、コードが煩雑になりますし、将来的に「ここのNotificationはどっちのNotificationだ?」となるので、自動変換をするまえに名前を変えておくことをおすすめします。
@noescape
が非推奨になり @escaping
が追加された
こちらの記事がよくまとまっています。
completionHandler
などを実装している時に警告されると思います。Xcodeの言うとおりに何も考えず @escaping
をつけていくと想定外の挙動をしたり、循環参照になったりするので注意が必要です。
Abolish ImplicitlyUnwrappedOptional type の影響
詳しくはこちら
簡単にサンプルコードで説明すると
// swift 2.2 let hello: String! = "Hello" let world: String! = "World" print("\(hello)\(world)") // => "HelloWorld\n"
このように、String Interpolation を利用して変数を展開することは多いと思います。Swift2.2では意図したとおりに変数が展開されていますが、このコードをSwift3.0以降で実行すると、
// swift3.0 let hello: String! = "Hello" let world: String! = "World" print("\(hello)\(world)") // => Optional("Hello")Optional("World")\n
となります。謎の Optional
が含まれていますね。これは、ImplicitlyUnwrappedOptionalの挙動が変わったことに起因しているのですが、String Interpolationした場合にはコンパイルエラーや実行時エラーにならないのでとても厄介です。。例えば。URLを
let searchWord: String! let url = "https://example.com/search/?q=\(searchWord)"
のようにString Interpolationを利用して組み立てている場合、エラーにはなりませんが望んだ挙動をしなくなります。とにかくエラーにもクラッシュにもならないのが厄介で、「Swift3化してコンパイルエラーにならないのに、なぜかアクセスが失敗する。」というときはこれが原因のときが多いです。
望まない動作を防ぐためにも文字列は(デバッグ時以外は)String InterPolationを利用せず +
で結合したほうが良いでしょう。そもそも例示したURLを組み立てる事例では、 URLComponents
と URLQueryItem
を使っていくのが良いでしょう。ちなみに、 UILabelなどに文字を流し込むときにも同様な問題があるので、画面上に大量の Optional("hoge")
が表示されないように、しっかりと置き換えとテストをしていくことが必要です。
まとめ
ここに示したのは大量に表示されるエラーの一例です。Swift3対応はとても大変な作業ですが、Swift2.3はXcode8.2までしかサポートされないので、ぐっと力をこめて移行してしまいましょう。Swift3->Swift4は少し楽になるはず(はず)ですので、今が頑張りどころです。
また、Swift3.0対応ではライブラリの整理ができる良い機会です。この機会にいらないライブラリを消したり、自作したりしちゃいましょう。
それでは、みなさまがSwift3と共によき新年を迎えられますよう。