Swift2.2からSwift3.0への変換を行ってみて

Xcode8のGMが出たので、Swiftを2.2から3.0へ変換しました。自動でコンバートするXcodeの機能を使いました。
変換箇所があらかじめ表示されるので、一通り見て気が付いたことを書いていこうと思います。

enumの要素の頭文字が小文字に統一
どっちが正解なのかなぁと思いながらプログラムを作成していた部分なのではっきり決めてくれて良かったです。

配列の初期化メソッド
配列の初期化によく使われる
init(count: repeatedValue: )が
init(repeating: count: )
になりました。順番が変わって、さらに現在分詞になりました。

NS抜け
NSBundle が Bundle へ変更
NSIndexPath が IndexPath へ変更
NSURL が URL へ変更
Appleに従うのみ。

タイプメソッド扱いしていたものがタイププロパティ扱いに
NSBundle.mainBundle() が Bundle.main へ変更
UIScreen.mainScreen() が UIScreen.main へ変更
UIDevice.currentDevice() が UIDevice.current へ変更
UIColor.whiteColor() が UIColor.white へ変更
//例
open class var white: UIColor { get }
従うのみ。

CoreGraphics衣替え
CGRectMakeなどのC言語ベースのCG***関数がSwift3.0では廃止になっています。
(旧)CGRectMake(***) が
(新)CGRect(***) へ変更、CGRect(***)の方はSwiftの構造体のinit命令呼び出しです。

(旧)CGPointMake(***) が
(新)CGPoint(***) へ変更、CGPoint(***)の方はSwiftの構造体のinit命令呼び出しです。

(旧)CGRectContainsPoint(CGRect rect, CGPoint point) が
(新)CGRectインスタンスに対するcontains(_:)メソッドへ変更

(旧)CGContextFillPath(CGContextRef cg_nullable c) が
(新)CGContextインスタンスのfillPath()に変更
など

UIControlState()
UIControlState.Normal は UIControlState() へ変更。
Normalの方がわかりやすい?

Boolインスタンス名にisが付いた
UIViewのhidden が isHidden に変更
UIScrollViewのscrollEnabled が isScrollEnabled に変更
Boolを返すものはisが付けられる変更です。従うのみ。

Stringの編集系メソッド
appendなどがマイナーチェンジされました。
こちらに詳しく書いてます。
Swift 3.0でStringはどうなったか

enumerated
配列からインデックスと要素を取り出すArrayインスタンスのenumerate() が enumerated() と過去分詞になった。
同じような変更は他にもいくつかあり、対象の変更、非変更によって原形か(ing型 or ed型)になるらしい。変更の場合は原型、非変更の場合は(ing型 or ed型)。
ingとedの違いは対象が()内にある場合はing、前にある場合(つまりメソッドを呼んでいるインスタンス)はedと思うが要調査。
abc.defed() //abcが対象?
abc.defing(ghi) //ghiが対象?

UIViewControllerの回転系
shouldAutorotateと
supportedInterfaceOrientations
がメソッドからcomputedPropertyになりました。
従うのみ。

メソッド第一引数特別扱いの廃止への対応はいろいろ
Swift2.2で
//定義
func testFunc(labelA: Int, labelB: Int)

//呼び出し
someInstance.testFunc(3, labelB: 5)
というメソッドがあった場合、
これをSwift3.0では
//定義部を変えない方法
//定義
func testFunc(labelA: Int, labelB: Int)    //←変更しない

//呼び出し
someInstance.testFunc(labelA:3, labelB: 5)    //←ラベル付きに変更
とするか
//呼び出し部を変えない方法
//定義
func testFunc(_ labelA: Int, labelB: Int)    //←ラベルなし明記に変更

//呼び出し
someInstance.testFunc(3, labelB: 5)    //←変更しない
といろいろな変更方法があります。
実際の変更方法もいろいろありました。例をいくつか挙げます。
//Swift2.2
applicationWillResignActive(application: UIApplication)

//Swift3.0
applicationWillResignActive(_ application: UIApplication)
applicationという言葉はすでにメソッド名にある。ラベルにいれたら重複。

//Swift2.2
tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath:NSIndexPath)

//Swift3.0
tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath)
tableViewがメソッド名になっている。ラベルにいれたら重複。

UIViewの描画メソッド
//Swift2.2
drawRect(rect: CGRect)

//Swift3.0
draw(_ rect: CGRect)
rectという表現がメソッド名から消えた。rectであることは引数がCGRectインスタンスであることで表現されているからと推測。

//Swift2.2
numberOfSectionsInTableView(tableView: UITableView) -> Int

//Swift3.0
numberOfSections(in tableView: UITableView) -> Int
メソッド名からTableViewが消えて、inがラベルに追加になった。

Stringの
//Swift2.2
drawInRect(rect: CGRect, withAttributes attrs: [String : AnyObject]?)

//Swift3.0
draw(in rect: CGRect, withAttributes attrs: [String : Any]? = nil)
Rectが消えて、ラベルにinを追加。

同じことを2度言わないの法則があるみたいですね。
これらのように、基本的な方針を基にその場に応じた判断をされています。

NSNumberリニューアル
Swift2.2ではビット数とメソッド名の関係がObjective-Cの束縛を受けていましたが、Swift3.0になって完全にSwiftになりました。
//Swift2.2
    public init(char value: Int8)
    public init(unsignedChar value: UInt8)
    public init(short value: Int16)
    public init(unsignedShort value: UInt16)
    public init(int value: Int32)
    public init(unsignedInt value: UInt32)
    public init(long value: Int)
    public init(unsignedLong value: UInt)
    public init(longLong value: Int64)
    public init(unsignedLongLong value: UInt64)
    public init(float value: Float)
    public init(double value: Double)
    public init(bool value: Bool)

    @available(iOS 2.0, *)
    public init(integer value: Int)

    @available(iOS 2.0, *)
    public init(unsignedInteger value: UInt)
    
    public var charValue: Int8 { get }
    public var unsignedCharValue: UInt8 { get }
    public var shortValue: Int16 { get }
    public var unsignedShortValue: UInt16 { get }
    public var intValue: Int32 { get }
    public var unsignedIntValue: UInt32 { get }
    public var longValue: Int { get }
    public var unsignedLongValue: UInt { get }
    public var longLongValue: Int64 { get }
    public var unsignedLongLongValue: UInt64 { get }
    public var floatValue: Float { get }
    public var doubleValue: Double { get }
    public var boolValue: Bool { get }

    @available(iOS 2.0, *)
    public var integerValue: Int { get }

    @available(iOS 2.0, *)
    public var unsignedIntegerValue: UInt { get }
//Swift3.0
    public init(value: Int8)
    public init(value: UInt8)
    public init(value: Int16)
    public init(value: UInt16)
    public init(value: Int32)
    public init(value: UInt32)
    public init(value: Int64)
    public init(value: UInt64)
    public init(value: Float)
    public init(value: Double)
    public init(value: Bool)

    @available(iOS 2.0, *)
    public init(value: Int)

    @available(iOS 2.0, *)
    public init(value: UInt)

    open var int8Value: Int8 { get }
    open var uint8Value: UInt8 { get }
    open var int16Value: Int16 { get }
    open var uint16Value: UInt16 { get }
    open var int32Value: Int32 { get }
    open var uint32Value: UInt32 { get }
    open var int64Value: Int64 { get }
    open var uint64Value: UInt64 { get }
    open var floatValue: Float { get }
    open var doubleValue: Double { get }
    open var boolValue: Bool { get }

    @available(iOS 2.0, *)
    open var intValue: Int { get }

    @available(iOS 2.0, *)
    open var uintValue: UInt { get }

似たような名前のメソッドの場合、統一できるものは統一
NSCoderの場合だと
//Swift2.2
public func encodeDataObject(data: NSData)
public func encodeObject(object: AnyObject?)

public func encodeObject(objv: AnyObject?, forKey key: String)
public func encodeBool(boolv: Bool, forKey key: String)
public func encodeInt(intv: Int32, forKey key: String)
public func encodeInt32(intv: Int32, forKey key: String)
public func encodeInt64(intv: Int64, forKey key: String)
public func encodeFloat(realv: Float, forKey key: String)
public func encodeDouble(realv: Double, forKey key: String)
public func encodeInteger(intv: Int, forKey key: String)
//Swift3.0
open func encode(_ data: Data)
open func encode(_ object: Any?)

open func encode(_ objv: Any?, forKey key: String)
open func encode(_ boolv: Bool, forKey key: String)
open func encodeCInt(_ intv: Int32, forKey key: String)
open func encode(_ intv: Int32, forKey key: String)
open func encode(_ intv: Int64, forKey key: String)
open func encode(_ realv: Float, forKey key: String)
open func encode(_ realv: Double, forKey key: String)
open func encode(_ intv: Int, forKey key: String)
Swift3.0ではencodeに(ほぼ)統一されました。数値のビット数がSwift2.2ではObjective-Cの影響を受けていますが、3.0で完全Swiftになりました。

ちなみに
UIViewの
open func bringSubview(toFront view: UIView)
open func sendSubview(toBack view: UIView)
のように、ラベル化しても
外部引数ラベル = 引数インスタンス
の関係になっていないパターンもあります。toFrontは操作の詳細の説明なので。

あと、Cスタイルのfor文をSwift3で書き換えるときにハマるパターンについてQiitaに書きました。
Cスタイルのfor文をSwift3で書き換えるときにハマるパターン

感想
以前から、Swiftでメソッドを作成するときは一つ目の引数に外部引数名と内部引数名に同じものを書くやり方をしていたので今回その部分は移行がスムーズになりました。
UIBezierPathのメソッドに、呼び出し方がmoveToPoint(***)からmove(to: ***)に変更されたものがあります。***がインスタンス名だけでは何の型のインスタンスかわからない場合もあるでしょう。情報を省くことは一長一短かと思います。
破壊工程、非破壊工程の違いによる、メソッド名に動詞の原形、現在分詞、過去分詞の使い分けが難しいです。ニュアンスが非英語圏にはわからない。

参考
Swift API Design Guidelinesの紹介(Swift 3版)

コメント