はじめに
これは手記である。
ここにSwift3の言語仕様についての言及はない。
また、具体的な現象を事細かに説明することもないだろう。
ここに示すのは、ただひたすらにXCodeが吐き出す果てることのない赤色のメッセージと対峙した記録である。
XCode自動変換
私はSwift3への移行(変換前は2.2)を始める前、XCode8が提供するSwift3への自動変換機能に楽観的な期待を抱いていた。
案外これだけでスルっと動くんじゃないのーと思っていたが、この考えは甘かった。
最初に試みた自動変換で、長い長い時間を待たされた後、出された結果は残念なものだった。
Swift製ライブラリの調査
ここで私は初めて重要なことを知ることになる。
それは、Swift2と3の混在は許されないということ。
つまり、cocoapodsで組み込んでいるSwift製ライブラリ達も全てSwift3へアップデートしなければいけないということだ。
私はライブラリ作成者のGitHubを1つ1つ確認し、Swift3バージョンがあるか確かめた。
驚いたことに大体のライブラリは既にSwift3版が用意されていて、最新版を取りこむだけでよかった。
swift3ブランチを明示的に指定してやる必要があるものは Podfile
を↓のようにした
cocoapods ver1.1.0
platform :ios, '9.0'
swift_version = '3.0'
use_frameworks!
target 'HogeApp' do
pod 'Hoge', :git => 'git@github.com:hoge/Hoge.git' , :branch => 'swift3'
end
Swift製ライブラリ対応
結局、移行作業で一番大変だったのはこの部分だった。
Swift3では関数名や引数についてのルールに変更があったようで、
ライブラリ達も作法にのっとりAPI(関数名と引数)がだいぶ様変わりしていた。
ライブラリが提供するAPIに対してはXCodeも自動変換してくれない。
ここは自力でやるしかないのだが、この作業がひたすら果てしなかった。(後述)
3.0は断念して2.3へ
自動変換されなかった箇所を、エディタ(Atom)の正規表現置換でちまちま変更していたのだが
半日ほど経って「これは1日2日で終わるものではない」と悟った。
スケジュールも迫っていたこともあり、3.0へのアップデートは諦め、マイナーアップデートである2.3への移行へと切り替えた。
2.3も3.0と同様、Swift製ライブラリを2.3版に切り替え、Xcodeで2.3への自動変換を施した。
いくつかエラーが出たが、これは軽微なものだったのですぐに対応できた。
2.2と2.3ほとんど差分がないため、テストも含めた移行コストは少なかったので助かった。
ひとまず、これを暫定対応とした。
再度3.0移行へ
再び、XCode自動変換とSwift製ライブラリを3.0に上げて、XCodeが吐き出す赤色のエラーを潰す作業に入った。
当たり前ではあるが、コンパイラが評価可能なコードは正常にコンパイルできる部分だけである。
コンパイルできない箇所があれば、それに関係して続くコードも評価されないことになる。
例えば、10個のエラーが出てたとして、その10個を解決すると、それによって新たに評価されるコードが生まれ、そして新たなエラーが20個生まれる、といった具合だ。
潰しても潰しても次々と生まれるしぶといエラーを1つ1つ解決していく。歯を食いしばりながら作業となった。
一括置換シェルスクリプト
最初はエディタ(Atom)を使用した正規表現置換で新コードに置き換えていたが、途中からこれでは効率悪いなと思い、簡易な一括置換シェルスクリプトを作り、Swiftソースファイルに対して
置換シェルスクリプトを実行 -> ビルド&確認 -> シェルスクリプトの編集 -> gitでソースを戻す -> 置換シェルスクリプトを実行
を繰り返しながら、作業を進めた。置換する項目が増えてシェルの実行時間が長くなってきたら、新しくシェルファイルを作成して、インクリメンタルに作業をしていった。
こんな泥くさい方法より他にもっと賢い方法があるのような気もするが、他にいい方法も思いつかなかった。(他のやり方があれば教えて欲しい)
下記に、一括置換シェルスクリプトで、変更箇所がもっとも多かったAlamofireの置換処理の一部を載せる。一時的なスクリプトなので、冗長な部分も多いが、、
スクリプトを使用する場合、ソースファイルを直接上書きするので注意されたし。
Alamofire (3.5.1) -> Alamofire (4.0.1)
reaplace.sh
#!/bin/bash
for fn in `ls ./*.swift`
do
echo "$fn"
## Alamofire
a='Alamofire\.request\(\.GET, (.*)\)'
b='Alamofire.request($1)'
perl -pi -e "s/$a/$b/g" $fn
a='Alamofire\.request\(\.POST, url\)'
b='Alamofire.request(url, method: .post)'
perl -pi -e "s/$a/$b/g" $fn
a='Alamofire\.request\(\.POST, url, parameters: (.*), encoding: \.JSON\)'
b='Alamofire.request(url, method: .post, parameters: $1, encoding: JSONEncoding.default)'
perl -pi -e "s/$a/$b/g" $fn
a='Alamofire\.request\(\.PUT, url, parameters: (.*), encoding: \.JSON\)'
b='Alamofire.request(url, method: .put, parameters: $1, encoding: JSONEncoding.default)'
perl -pi -e "s/$a/$b/g" $fn
a='Alamofire.request\(\.DELETE, url, encoding: \.JSON\)'
b='Alamofire.request(url, method: .delete, encoding: JSONEncoding.default)'
perl -pi -e "s/$a/$b/g" $fn
a='Alamofire.request\(\.DELETE, url\)'
b='Alamofire.request(url, method: .delete)'
perl -pi -e "s/$a/$b/g" $fn
a='response : Response<NSData, NSError>'
b='response : DataResponse<Data>'
perl -pi -e "s/$a/$b/g" $fn
a='case \.Success'
b='case .success'
perl -pi -e "s/$a/$b/g" $fn
a='case \.Failure'
b='case .failure'
perl -pi -e "s/$a/$b/g" $fn
a='let parameters: \[String:AnyObject\]'
b='let parameters: Parameters'
perl -pi -e "s/$a/$b/g" $fn
done
実行方法
シェルスクリプトをswiftソースのディレクトリに置いて、ターミナルから実行
sh replace.sh
2回目のXCode自動変換
ライブラリ関連のエラーをだいたい潰し終えた段階で、もう1度XCodeの自動変換をかけた、1回目の自動変換のときにはコンパイラから見えてなかったコードも自動変換される。
ビルド成功
全ての赤色エラーを潰してビルド成功。エラーの全量がわからず、終わりが見えなかったが、ようやく第1段階をクリア。
テストとリカバリ
ホッとしたのもつかの間。全面的にコードが書き換えられているので、アプリケーションの動作確認を1通り実施をすると、実行時クラッシュ、挙動の不具合と問題がかなり出てきた。有値オプショナルの挙動が変わっていたり、Objective-C製のライブラリがどうしても動かないので、代替のものを探したり、大量に発生した黄色のメッセージ(Warning)に対応したりする。
移行を終えて感想
めっちゃ大変やった。
AlamofireをラップしたAPIクライアントを作っておけばよかった、や
ライブラリのアップデートをこまめにしておけばよかった、などいろいろ課題も見つかった。
Swift2.3のサポートはXCode8.2までのようなので、早めの移行を検討されたし。