euphonictechnologies’s diary

Haskell超初心者の日記です。OCamlが好きです。

follow us in feedly

Swift 1.2から2.2でウラシマ状態 - ビルドできるように修正する

久しぶりにXCodeを起動しました。

昔のプロジェクトをインポートしたところ"最新のswiftに自動で変換しますか?"と余計なお世話をぶちかましてきたのでスルーしてビルドしたところビルドエラーの嵐。後方互換性とか関係ねえからっていうAppleの教えが心に響きます。新しい機能は拡張で導入するHaskellが懐かしいです。

というわけで自分のために直した箇所をメモしていこうと思います。確か前回のブログを読むと使っていたコンパイラは1.2なので1.2->2.2です。

$ swift --version
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)

一応確認してみました。

違いたち

  • touchesBegan

修正前

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)

修正後

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)

お前overrideつってるけどoverrideしてねえぜと言われて調べてみたらこの有様です。

  • 関数宣言だけかなんかしんねーけどすべての変数に名前をつけろよこのデコスケ野郎

Unnamed parameters must be written with the empty name '_'

馬鹿じゃねーのか。明らかにredundantなんですけど、大丈夫なんですかね。とりあえず言われたとおり_:をつけて名前の無い変数ですという宣言をします。他言語ブリッジに対する嫌がらせですかね。

  • Unknown attribute 'asmname'

名前が変わって@_silgen_nameになったようです。xcode - How to use @asmname() in Swift 3.0 - Stack Overflowの回答者の人がその変更が行われたコミットとチェンジログにもリンクを貼ってくれています。とても偉い。

  • 'reduce' is unavailable: call the 'reduce()' method on the sequence

普通の人間であれば畳み込みは

reduce [1, 2, 3, 4, 5] 0 add

と書くところですが、この頭の狂った言語では

[1, 2, 3, 4, 5].reduce(0, add)

と書くらしい。頭大丈夫?関数がファーストクラスオブジェクトって言いたいだけなのはわかった。2016年になっても関数型言語の普及ははるか遠い。部分適用するときに

[1, 2, 3, 4, 5].reduce()

って書くのかな?と思ったのですが、Swiftには部分適用はクロージャ作って自分でカリー化しないかぎりなさそうなので別にどうでも良さそうでした。頭に血が上ってすみません。Swiftは手続き型言語なのを忘れていたのです。この書き方だとメソッド呼び出しをチェーンするときに便利ですね。

  • Missing argument label 'combine' in call

さて、上の例はまだ不完全です。なぜならば

[1, 2, 3, 4, 5].reduce(0,combine: add)

ふたつ目の引数には必ずラベルを付けて呼ぶ決まりです。これは特に文句をつけることもないかな、と思います。OCamlとかでもJane Street Coreとか使っていてラベル付き引数関数を多用していると手が自然につけていたりします。多分CとかJavaから来た人はびっくりするんじゃないでしょうか。ただラベルつけるかつけないか選べないのは鬱陶しくて

some2dMatrix.getElement(x, y: y)

と変なy:がつきます。これは少し鬱陶しい。タプルでやれってことだと思います。 とまれ、結局Objective-C臭さを消せなかったSwiftです。OCamlなんとかっていうのは

String.sub "Hello" ~pos:0 ~len:4

こういうのですね。にょろかわいい。

  • supportedInterfaceOrientations() -> Intはもうないよ

ガイシュツだと思いますが

supportedInterfaceOrientations() -> UIInterfaceOrientationMask

になりました。なので昔無理やり

Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)

してたキャストは外して.rawValueのプロパティで返さなくても大丈夫です。オブジェクトの返し方の最適化でも入ったんでしょうか。

  • NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)

これは

Argument labels '(contentsOfFile:, options:, error:)' do not match any available overrides

というエラーになります。簡単に言うとerror:がなくなったのでこれを外せばよいのですが、これはSwift1->2でエラーハンドリングが充実したことによって変わった模様です。

www.hackingwithswift.com

というわけで、きちんとハンドルするようにしてみます。しかし足りない頭で私見を言えばこの場合nilを返せばいいわけで、なんでtry-catchが必要なんでしょうか。それがOptionalのいいところだったはずなのですが。多分エラーの種類によって処理を変えたいということなんだと思いますが型による解決を図るならばEither型とかある…といっても多分Swiftプログラマ混乱しちゃうだろうし無理だろうな。Optionalが特別な型なおかげでこういう自由な拡張ができなくなってしまったのは少し残念です。普通にtryキャッチしましょう。まずerror:を外します。

var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)

とすると

Call can throw, but it is not marked with 'try' and the error is not handled

と出ます。すぐにエラー処理をせよと。遅延させるなと。

guard let sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
    return nil
}

こういう書き方でいいんでしょうか。これは新しいguard句です。メイヤー先生もニッコリですね。もちろん私もこういう表明的プログラミングは好きです。ここでguard ~ elseと出てきています。これはassertです。elseでassert失敗した時の処理を書きます。ifで等価なステートメントがかけますね。つまり

if let sceneData = try? ~ {
    // do nothing
} else {
    return nil
}

ってことですね。参考:swift - try, try! & try? what’s the difference, and when to use each? - Stack Overflow

marked with 'try'

と言うのは参考リンクにあるtry!try?の表明をせよということです。try!は「ダメだったら死んじゃお!」でtry?「ダメだったら見なかったことにしちゃお!」ってことです。try?の場合nilが帰るのでOptionalの話に戻れます。そこをguardとかif letとかで処理を分けるわけです。ちう事で、try?は上で文句言った「Optional型もっと使えよ!」に対する答えになっています。文句言ってすみません。死にたくもシカトも嫌な場合は

do {
    // codes can throw
} catch {
    // error handling
}

と普通にtry-catchもできます。

  • StringはSequenceじゃないよ

Type String does not conform to protocol 'SequenceType'

となりましたので、

for c in fromText.characters {
...

としましょう。The Swift Programming Language (Swift 2.2): Strings and Characters のWorking with charactersにも書かれています。

  • 'NSDictionary' is not implicitly convertible to '[NSObject : AnyObject]'; did you mean to use 'as' to explicitly convert?
UINavigationBar.appearance().titleTextAttributes = titleDict as [NSObject : AnyObject]

というステートメントにエラーが出ました。この場合素直にas [NSObject : AnyObject]を消して自動で教えてくれる修正をしましょう。この場合

UINavigationBar.appearance().titleTextAttributes = titleDict as! [String : AnyObject]

という修正になりました。型推論便利ですね。確かに1.2の頃よりも型推論機が強力になっているような気がしています。

  • Downcast from 'UITableViewCell?' to 'UITableViewCell' only unwraps optionals; did you mean to use '!'?

まず混乱したのはビックリマークなのかハテナなのか解んなくなっちゃうところです、末尾が。

なんか

return tableView.dequeueReusableCellWithIdentifier(reusableCellId) as! UITableViewCell

としていたところが変なダウンキャストなしでも大丈夫になったみたいです。素直にunwrapだけに変えましょう。

return tableView.dequeueReusableCellWithIdentifier(reusableCellId)!

これも指摘されている部分を素直に削除すると修正方法を教えてくれます。素直に従っちゃいましょう。もしくは先に上げたナイスなエラーハンドリングを書きましょう。

全部赤いやつ修正できた!

これらが私のコードにあったエラーでした。すべて修正してビルド完成です。つぎは黄色いワーニングを修正していきます。

github.com

Swift Pocket Reference: Programming for Ios and OS X: Covers Swift 2.1

Swift Pocket Reference: Programming for Ios and OS X: Covers Swift 2.1