Xcode 6.3か6.4の時代に作ったiOSアプリがあってメンテしているんですけれど、iOS 7.xに対応する必要がありCocoaPodsも0.39*1環境でしか使えず、Podfileのuse_frameworks!
も使えませんでした。
最近、deploy targetの見直しがあってようやくiOS 7.xを外すことができました。Xcode 8への移行に伴いCocoaPodsもアップデートしてSwift 2.3に対応しました。
ただし、Swift 3.0対応はスルーしていました。なぜSwift 3.0対応をスルーしたかというと、手を付けるとしんどそう という感覚が伝わってきたからです。プロジェクトもひと段落したのでこの隙に!とばかりにSwift 3.0対応してみました。
結論としては、Swift 3.0対応はやっぱり大変でした。細かいところが多すぎるので雑に書いてみました。
やったことの一覧
本記事は、Swift 3.0対応してから1週間後に書いているのと作業しながらメモを取らなかったのもあって、思い出しながら書いているため記載が正しくない部分があるかもしれません。また意図的にボカして書いている部分もあります。ご了承ください。
Podfileを変更した
Swift 3.0対応するにあたって使用するライブラリを変えたところがあってPodfileに手をいれました。*2
# Uncomment this line to define a global platform for your project platform :ios, '8.0' def shared_pods pod 'RealmSwift' pod 'Google/Analytics' pod 'TTTAttributedLabel' pod 'Dollar' pod 'SwiftyJSON' end target 'staging' do use_frameworks! shared_pods end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = "3.0" end end end
ライブラリの変更
- RealmからRealmSwiftへ移行しました
- RLMObjectで扱うよりもObjectで扱った方が楽なので移行したような?
- swift-jsonからSwiftyJSONへ移行しました
- Swift 3.0対応されていなかったため
- Dollarをソースコードの切り出しでプロジェクトに含めていたのをCocoaPodsを使ってインストールするようにしました
- 管理を楽にするため変更
ライブラリを変更したことによってコードを変更する必要がありました。ただこれは本質的にはSwift 3.0対応ではないですね。
ソースコードの変更(細かいビルドエラー修正)
地道なビルドエラー修正でした。
NSData
からData
へのprefixのNSを取るだけの簡単なケースUnicodeScalar
からString
への変換方法が変わってしまい調査が必要になったケース- enumの頭文字が小文字に変更になった
UIColor.magentaColor()
がUIColor.magenta
のように変更になった
ソースコードの変更(引数自体が変更になっているケース)
最初の時点でビルドエラーの数は2,000くらいあったような(表記は999+がMAXでした)で、通常Xcodeの赤丸をポチポチクリックするだけで済むようなケースでも、ソースコードを1行変更することで再ビルドされるまで赤丸が消えてしまっていました。
UIKitのdelegate名の変更が意外と面倒くさかったのを覚えています。下記のように引数自体が変更になっているケース。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
↓
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
ソースコードの変更(引数のラベルを省略する_が追加されているケース)
下記のように引数自体は同じだけど引数のラベルを省略するために_
を追加しないといけないケースはたくさんありました。
func applicationDidEnterBackground(application: UIApplication) {
↓
func applicationDidEnterBackground(_ application: UIApplication) {
ソースコードの変更(UITableViewのdelegateが変更されているケース)
iOSアプリといえばUITableViewだ、と個人的に思っているのですが、こっちも変更がいくつかありました。
メソッド名が変わっている。
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
↓
override func numberOfSections(in tableView: UITableView) -> Int {
引数が変わっている。
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell?
↓
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell?
ソースコードの変更(CGContextXXXXの使い方の変更)
画像に触るシーンが多くてCGContextXXXXメソッドを多用していたのですが、CGContextTranslateCTMのような関数からcontextクラスにインスタンスメソッドが生えた形に変わったのも大きな変更と言えそうです。
let context = UIGraphicsGetCurrentContext()! CGContextTranslateCTM(context, size.width, size.height) CGContextScaleCTM(context, -1.0, -1.0) CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), imageRef)
↓
let context = UIGraphicsGetCurrentContext()! context.translateBy(x: size.width, y: size.height) context.scaleBy(x: -1.0, y: -1.0) context.draw(imageRef, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
ソースコードの変更(GCD)
dispatch_async(dispatch_get_main_queue(), { () -> Void in //なんか非同期な処理 })
↓
DispatchQueue.main.async {
//なんか非同期な処理
}
まとめ
一通り置換して xcodebuildを使ってビルドしてエラーが発生したところを逐一対応する、ということを延々と繰り返しました。
Xcodeのマイグレーションツールが信用できなくてほとんどを手作業したため手間がかかったとも言えるのですが、なぜコンバートが信用できなかったかというと、hoge(1, 2)
みたいに書かれていたコードをhoge(2, 1)
のように引数の順番が変えられてしまったところに起因しています。
全てビルドを通してようやくSwift 3.0への移行時のつまずきみたいなのが見えてきたので言えることなのかもしれませんが、一旦マイグレしてからおかしな変更を加えられてしまったところを修正した方が楽だったのかもしれません。
追記(11/21 21:25)
お世話になっている_monoさんに本記事について言及されていました。*3
Swift 3のコンバーターは保守的で、確実に直せる簡単なパターンしか対応していないように見えた(挙動的にもコンバート処理のソースコード的にも)ので、こんな大胆な間違えあり得るのかな?と疑ってしまう( ´・‿・`)
— 🐶 mono - iOS/SwiftとFlutterとラブラドールの雑種 (@_mono) 2016年11月21日
再現パターンとかバグレポートとかあったら知りたい( ´・‿・`) https://t.co/BAg8jVE1i5
もうすでにビルド通しちゃっていたのでどこで問題が発生していたんだっけ?と一瞬頭を抱えましたが、Swift 3対応前の状態に戻して記憶を頼りに「どこでエラー出てたんだっけなぁ……」と探して、ようやく見つけることができました。
無事、_monoさんに伝えることができました。
.@ch3cooh さんに教えていただきつつ、シンプルな再現コード書いた( ´・‿・`)
— 🐶 mono - iOS/SwiftとFlutterとラブラドールの雑種 (@_mono) 2016年11月21日
確かに間違ってる( ´・‿・`)https://t.co/338dX4bq8R の変更に追従すべきところで、なぜか関係無い第2・第3引数入れ替えが促されてる( ´・‿・`@_mono pic.twitter.com/XLqhVfoY1V
第2・第3引数のラベルを_で隠していなければおかしくならない( ´・‿・`)
— 🐶 mono - iOS/SwiftとFlutterとラブラドールの雑種 (@_mono) 2016年11月21日
ラベルはあまり省略しないとはいえ、全パラメーターのラベルを省略するケースはある(maxみたいな関数)ので、以前のラベルの仕様で全引数のラベルを省略するメソッド書いてた場合、これに引っかかりそう。 https://t.co/UpknRyxexD
以上、Swift 3.0対応を振り返った記事を書いてみました。
関連記事
この他にもiOSアプリ開発で見つけたネタや悩んだ内容など紹介しています。Tipsをまとめておりますのでこちらのページをご参照ください。