酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Swift 3.0対応は大変だった

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対応前の状態に戻して記憶を頼りに「どこでエラー出てたんだっけなぁ……」と探して、ようやく見つけることができました。

f:id:ch3cooh393:20161121213648p:plain

f:id:ch3cooh393:20161121213335p:plain

無事、_monoさんに伝えることができました。

以上、Swift 3.0対応を振り返った記事を書いてみました。

関連記事

この他にもiOSアプリ開発で見つけたネタや悩んだ内容など紹介しています。Tipsをまとめておりますのでこちらのページをご参照ください。

*1:結構昔のバージョンです

*2:全てのライブラリを記載していません

*3:iOSブログじゃないのに読まれているなんて……!