bbs
oopers.com
5441 OOPer: 2015-08-14 10:33:15 1
Swift2[1]: OptionSetType
という訳でDev Forumsへのネタ提供のネタも尽きてきたので、逆提供してもらうことにした。

Swift 2の鳴り物入りの新機能:
- エラーハンドリング
- 可用性チェック
- プロトコル拡張
等々に加え、地味に色んな点が変更になっていて、そこら変でつまづいている人も多い。Xcode7の正式リリースも近づいてるんで、「そろそろうちの社でも本気でSwiftの採用を検討しようか」なんて動きも出ているようなんで、転ばぬ先の杖と言うことでいくつかDev Forumでよく上がっていた失敗例を挙げていこうかと思う。第1回は(うちのBBSの性格上、第2会は無い可能性もあるのであまり期待しないよう)OptionSetTypeから。

こんなコードがSwift2では動かない。
let dateComponentsFormatter = NSDateComponentsFormatter()
dateComponentsFormatter.allowedUnits = NSCalendarUnit.Hour | NSCalendarUnit.Minute | NSCalendarUnit.Second


これ、Swift1.xでは、RawOptionSetTypeとしてインポートされていたNS_OPTIONSがOptionSetTypeに変更になったため。Swift2だとこんな書き方になる。

let dateComponentsFormatter = NSDateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.Hour, .Minute, .Second]


中身が単に整数ビットマスクだとわかっているC寄りのプログラマーにはピンとこないかもしれないが、Swift enumのassociated valueを見て、「これってPascalの可変レコード型だよね」と思った人には、「Pascalのset型じゃん」と思われることだろう。リテラル表記はSwift1.2で導入されたSet型と共通(Array型とも共通ってのがちょっと分かりづらい)。

ちなみに空要素は、Objective-Cでは0、Swift1.xではnil (.allZerosだかなんだかを使っている人も多かったらしい、私は使ったことが無いが)だったのが、空集合(空配列)と同じに[]に変わっちゃったりなんかしている。Xcode7のメソッド呼び出し時のタイプエラーは未だにとんでもない場所を示したりするので、options:引数をnilにしたせいでエラーになっているの見抜けずに、違う場所を試行錯誤で変えまくっている人も多い。まずは、options: nilをoptions: []に変えてみてちょ。

プロトコル拡張はデフォルト実装としても使えるので、自分でコードを書かないといけない部分はRawOptionSetTypeの頃に比べて減ったのだが、それでもまだこんな書き方をしないといけない。
struct MyOptionSet: OptionSetType {
    var rawValue: Int
    init(rawValue: Int) {self.rawValue = rawValue}
    static let MyOptionA = MyOptionSet(rawValue: 1)
    static let MyOptionB = MyOptionSet(rawValue: 2)
    static let MyOptionC = MyOptionSet(rawValue: 4)
}

普通要るでしょってvar rawValue: Intをイニシャライザ込みで宣言しないといけないのもうざいが、後半の定数地の宣言もかなりうざい。せめてenumのように、
struct MyOptionSet: OptionSetType {
    var rawValue: Int
    init(rawValue: Int) {self.rawValue = rawValue}
    case MyOptionA = 1
    case MyOptionB = 2
    case MyOptionC = 4
}

とか書けるようにならないものか。まだFeature Requestは書いていない。