Swift3(3.1)移行 注意点 まとめ

  • 5
    いいね
  • 0
    コメント

バージョンとか

Xcode 8.3.1
Swift 3.1
作業日数 2日

※Swiftのバージョンは下記のコマンドで調べられるよ!
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -version

参考
http://qiita.com/s_emoto/items/30d5203db641857a1f75

前提的な

実際に自分のプロジェクトででたエラーのまとめになるので、プロジェクトごとに別のエラーが出たり出なかったりすると思います。

  • コンパイルエラーのかんたんな修正方法 該当のエラーを選択しながら、Xcode の Editor メニューに Fix All in Scope という項目があってこれを選択するとファイルの赤丸を一気に全部なおしてくれます。(Command + Option + Control + F) ※ただし、結構 な頻度で動かなかったりするのでなんかエラー数が減らないなぁと思ったら1つずつ自動修正するといいです。あと、連打するとなおったりとかもします。

http://qiita.com/takayama/items/096d7fa6780f232da142#%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E7%B0%A1%E5%8D%98%E3%81%AA%E4%BF%AE%E6%AD%A3%E6%96%B9%E6%B3%95

  • Swift3の命名規則とかに関しては下記がすごくよくまとまってます。

【Swift 3 を書くときに知っておきたい API デザインガイドライン】
https://www.slideshare.net/tomohirokumagai54/swift-3-api-loveswift-akibaswift

本題

Xcodeのバグ?編

一部privateがfileprivateに書き換えられない箇所があった

swift2
    private let test = "test"
バグ
    private let test = "test"
swift3
    fileprivate let test = "test"

なんか知らないが一部fileprivateになっていない変数や関数がありました。
そんな変数このクラスにないよ的なエラーメッセージがでたらこちらを疑うといいです。
unrecognized selector sent to instance

@IBOutlet, @IBAction の記述が消える

swift2
    @IBOutlet private weak var moreButton: UIButton!
    @IBAction private func moreButtonTapped(sender: UIButton) {
    }
バグ
    fileprivate weak var moreButton: UIButton!
    fileprivate func moreButtonTapped(_ sender: UIButton) {
    }
swift3
    @IBOutlet fileprivate weak var moreButton: UIButton!
    @IBAction fileprivate func moreButtonTapped(_ sender: UIButton) {
    }

privatefileprivate に修正する際に、@IBOutletの記述が削除されてしまい、アプリは起動するが起動直後に落ちます。
再度@IBOutlet@IBActionの記述をすればIBとの接続はやり直さなくても動きます。
※原因は不明ですが、再度つなぎ直さないといけない場合もありました。

IBAction の第1引数が省略されない

swift2
    @IBAction func moreButtonTapped(sender: UIButton) {
    }
バグ
    @IBAction func moreButtonTapped(sender: UIButton) {
    }
swift3
    @IBAction func moreButtonTapped(_ sender: UIButton) {
    }

Swift3では第1引数を省略する場合は明示的に_を記述するようになったのですが、一部IBActionで_の記述がないために、アプリが落ちてしまいました。こちらも_を記述すれば自動的に接続されて動くようになります。
※こちらも原因は不明ですが、再度つなぎ直さないといけない場合もありました。

tableViewのdelegateメソッドやdataSouceメソッドの第一引数が省略されない&メソッド名が変わったけど 置換されない

swift2
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    }
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    }
バグ
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    }
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    }
swift3
    func numberOfSections(in tableView: UITableView) -> Int {
    }
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    }

optionalなメソッドだったりすると実装していなくてもエラーなどがでないので気づきにくかったです。
なぜか知らないがtableViewのデータが片っ端から表示されなくて、API周りから確認していったらまさかのdelegateメソッドが実装されていない(メソッド名が変わったので呼ばれない)だったとは....
breakpointをしかけても無反応だったのでめっちゃ焦りました。

Enum で一部 LowerCamelCase になおらないものがありました

swift2
    enum Test: Int {
        case Apple = 0
        case Banana
    }

    switch test {
        case Test.Apple
            break
        case Test.Banana
            break
    }
バグ
    enum Test: Int {
        case apple = 0
        case Banana
    }

    switch test {
        case Test.Apple
            break
        case Test.banana
            break
    }
swift3
    enum Test: Int {
        case apple = 0
        case banana
    }

    switch test {
        case Test.apple
            break
        case Test.Banana
            break
    }

なぜかはよくわかりませんが、UpperCamalCaseのままの部分があったり、LowerCamelCaseに変換されている部分があったりしてエラーが大量に出ました。caseとかでプロジェクト全体を検索して1つ1つ修正していきました...

Swift3の仕様編

dispatch_async を使わなくなりました

swift2
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) 
dispatch_after(dispatchTime, dispatch_get_main_queue(), { 
    // your function here 
})
swift3
// to run something in 0.1 seconds

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    // your code here
}

http://qiita.com/koogawa/items/f6e8f31ca549dbf55e83

AnyObjectで返ってきていた部分がAnyになりました

※もしかしたらアプデしたライブラリの仕様変更が原因かも

swift3
    let anyObject = any as AnyObject
    let test = anyObject["test"]

asで簡単にキャストできるので一旦は問題ないかと思います。

型名取得にdynamicTypeを使わなくなりました

swift2
    let text = "Hello!"
    String(text.dynamicType)
swift3
    let text = "Bonjour!"
    String(describing: type(of: text))
OR
    String(describing: String.self)

こちらXcodeが自動で修正してくれますが、今回のプロジェクトではことごとく変換時に修正されてしまいました...
これに関しては手動で直したほうがいいです。

http://dev.classmethod.jp/smartphone/iphone/swift-3-type-of/

NS prefix がなくなった(ほとんど)ことで名前が衝突しました

※これは普通は起こらないと思いますが...

swift2
    let URL = NSURL(string: "https://sample.com")
swift3
    let URL = Foundation.URL(string: "https://sample.com")

変数名がURLだったために、URLクラスと名前が衝突してしまいました。Foundationをつければ動きますが、こういったことが起きないように命名規則はAppleに従っておきましょう...

NotificationのNameの指定がStringからNSNotification.Name 型に変わりました

swift2
        NSNotificationCenter.defaultCenter().
            addObserver(self,
            selector: #selector(CaptureInfoFirstViewController.willEnterForeground),
            name: UIApplicationWillEnterForegroundNotification,
            object: nil)
swift3
        NotificationCenter.default.
            addObserver(self, 
            selector: #selector(CaptureInfoFirstViewController.willEnterForeground), 
            name: NSNotification.Name.UIApplicationWillEnterForeground, 
            object: nil)

nameの部分が変わっています。一旦の場合は上記で良いですが、ちゃんと書き直す場合は下記のリンクを参考に修正したほうがいいです。

http://qiita.com/mono0926/items/754c5d2dbe431542c75e

NSDateがDateに変わったことでNSDate-Escortが使えなくなった

NSDate型をライブラリで拡張していたのですが、Date型に変わってしまったため、拡張したメソッドやらプロパティやらが使えなくなってしまいました。なので、SwiftでDateの拡張として書き直しました。(下記のリンク参考)
よくよく考えたら作り直さなくてもライブラリの拡張しているクラス名をNSDateからDateに変えればよかったのかな?Objective-cからだとDateってつかえるのか?

http://qiita.com/rd0501/items/d9a5bde85ec0b12de983

MutableMutableCollectionのextensionでshuffleInPlaceとか実装しているとエラーになる

http://stackoverflow.com/questions/37843647/shuffle-array-swift-3
上記とかを参考にしてMutableMutableCollectionのextensionでshuffleInPlaceを実装しているとエラーが出ます。

swift2
extension MutableCollectionType where Index == Int {
     mutating func shuffleInPlace() {
         // empty and single-element collections don't shuffle
         if count < 2 { return }

         for i in 0..<count - 1 {
             let j = Int(arc4random_uniform(UInt32(count - i))) + i
             guard i != j else { continue }
             swap(&self[i], &self[j])
         }
     }
}
swift3
extension MutableCollection where Index == Int {
     mutating func shuffleInPlace() {
         // empty and single-element collections don't shuffle
         if count < 2 { return }

         for i in startIndex ..< endIndex - 1 {
             let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
             guard i != j else { continue }
             swap(&self[i], &self[j])
         }
     }
}

最後に

Swift3対応は大変だった気がしますがまとめて見るとそうでもなかったですね。
大体2日ぐらいあれば(プロジェクト規模によりますが)修正できました(一旦動かせるところまで)。
罠的なところが多いので他の移行記事などを読んでからとりかかると罠に引っかからずに、比較的はやく移行できるかもしれません。

参考

【Swift2.2 から Swift3 へ頑張って移行した話|Qiita】
http://qiita.com/takayama/items/096d7fa6780f232da142