いものやま。

雑多な知識の寄せ集め

Xcode 7にアップデートしてみた。

Xcode 7にアップデートして、それによってSwift 2.0への変更を含め、いくつか修正が必要だったので、それについて。

マイグレーション

まず、Xcode 7にアップデートして既存のプロジェクトを開くと、Swift 2.0に合わせて変更を行うかどうかを確認するダイアログが表示される。
これを使うと、簡単に変更が出来る。

ただし、コミットしていない変更がある場合、変更が混ざってしまってよく分からなくなってしまうので、一度コミットしたり、あるいは変更の退避(stash)をしてから行った方が安全。

あとからマイグレーションを行いたい場合、メニューの"Edit" - "Convert" - "To Latest Swift Syntax..."を選択すればいい。

それと、マイグレーションを行ってもエラーが残ることはあるので、そういうところについては手動で修正が必要。
あと、自動で修正された部分についても、ちゃんと確認しておいた方が無難。

コードの修正

print()の変更

まず、シンプルなところとして、print()の変更から。

以前は、

  • 改行なしでコンソールに文字列を出力するには、print()
  • 改行ありでコンソールに文字列を出力するには、println()

となっていたけれど、アップデートしたことで、println()関数はなくなって、print()関数のみになった。
print()関数を使うと、デフォルトで改行がされる。

// 変更前
println("hoge")
// 変更後
print("hoge")

以前のprint()と同様に、改行をさせたくない場合、terminater:という引数で空文字列を指定するといい。

// 変更前
print("hoge")
// 変更後
print("hoge", terminator: "")

なお、printの引数は可変で、separator:を指定することもできる。

print("foo", "bar", "baz", separator: ", ")
// => "foor, bar, baz"を出力して改行

文字列の連結

文字列を連結するときに、以前はString#extend(_: String)というメソッドがあったのだけど、この名前がString#appendContentsOf(_: String)に変更されていた。
(文字を連結するString#append(_: Character)はそのまま使える)

ファイル操作のコードの変更

ファイル操作のAPIの多くが、エラーへの参照を引数に取ったり、nilを返したりするのではなく、例外を投げるように変更されているみたい。
本当はちゃんと処理した方がいいけど・・・失敗することがまずない場合、try!を使って例外を無視(例外が起きた場合は死亡)させるといい。

一例は、次のような感じ。

/* Application SupportディレクトリのURLを取得 */
// 変更前
let supportDirectory = fileManager.URLForDirectory(.ApplicationSupportDirectory,
                                                   inDomain: NSSearchPathDomainMask.UserDomainMask,
                                                   appropriateForURL: nil,
                                                   create: true,
                                                   error: nil)!
// 変更後
let supportDirectory = try! fileManager.URLForDirectory(.ApplicationSupportDirectory,
                                                        inDomain: NSSearchPathDomainMask.UserDomainMask,
                                                        appropriateForURL: nil,
                                                        create: true)

失敗するかもしれないけど、その場合、従来通りnilを返させたい場合には、try?を使うといい。

/* ファイルの内容からNSStringを生成 */
// 変更前
let string: NSString! = NSString(contentsOfURL: url,
                                 encoding: NSUTF8StringEncoding,
                                 error: nil)
// 変更後
let string: NSString! = try? NSString(contentsOfURL: url,
                                      encoding: NSUTF8StringEncoding)

ちょっと困ったところとしては、NSFileManager#createDirectoryAtURL(_: NSURL, withIntermediateDirectories: Bool, attributes: [String : AnyObject]?)やNSFileManager#removeItemAtURL(_ : NSURL)。
これらも失敗した場合は例外を投げるようにAPIが変更されているのだけど、その「失敗」にどこまで入っているのかがちょっと分からない。
というのも、以前の場合、これらは、例えば作ろうとしているディレクトリがすでに存在していたり、あるいは逆に、削除しようとしているファイルがすでにない場合、単にtrueを返してた。
けど、APIが変更されて、こういった場合に例外を投げるのかどうかが分からない・・・
投げられる例外にどんなものがあるのかが分かれば、こういった場合に例外が投げられるのかどうかの確定が出来るのだけど。

ということで、基本的に失敗することはないのだけど、元々は「成功」扱いしていたけど例外が投げられるように変更されている可能性があるファイル操作については、try!ではなくtry?を使って、その例外を無視するようにした。
(例外が投げられないのであれば、本当はtry!を使いたい)

/* ファイルの削除 */
// 変更前
fileManager.removeItemAtURL(url, error: nil)
// 変更後
_ = try? fileManager.removeItemAtURL(url)
// 以下もOK(Xcodeのマイグレーションだと、こうなる)
do {
  try fileManager.removeItemAtURL(url)
} catch _ {
}

タッチ処理のコードの変更

UIResponder#touchesBegan(_: Set, withEvent event: UIEvent?)などのインタフェースが、UIResponder#touchesBegan(_: Set, withEvent event: UIEvent?)に修正されている。
なので、

let touch = touches.first as! UITouch

というキャストは不要になって(というか、やるとエラーになる)

let touch = touches.first!

でOKになった。

その他の変更

他にも、クロージャの型がちょっと変わったりというのがあった。
(UIAlertActionのイニシャライザのhandler:引数の型が(UIAlertAction!) -> Voidから(UIAlertAction) -> Voidになったりとか)
あと、CGPoint.zeroPointがCGPoint.zeroになったり。
まぁ、基本的にはマイグレーションで変更されるので、大丈夫だと思うけど。

あとは、do-whileがrepeat-whileに変わったので、使っているなら変更が必要。

他、guard節が使えるようになったので、assert()を使っていた部分については、本当はgurard節に置き換えた方がいい感じ。
ただ、今回は変更していない。

ユーザインタフェース

Storyboardやxibファイルは、一度エディタで開いておいた方がいいみたい。
XML内でツールバージョンなどがちょこっと変わるみたいで、この変更が他の変更に紛れると、あとでマージがややこしそうだから。

ビルドの警告対応

エラーもなくなってビルドを行ってみると、もしかしたら警告が出るかもしれない。
自分の環境では、2つ警告が出た。

まず1つ目は、リンクの警告。

ld: warning: directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.0.sdk/Developer/Library/Frameworks'

調べてみると、これはTestのビルド設定のサーチパスの問題みたい。
プロジェクトの"TARGETS" - "xxxTests" - "Build Settings" - "Search Paths" - "Framework Search Paths"に指定されている間違ったパスを削除すればOK。

もう一つは、次のような警告。

warning: All interface orientations must be supported unless the app requires full screen.

これについては、プロジェクトの"TARGETS" - "xxx" - "General" - "Deployment Info" - "Requires full screen"にチェックを入れれば、とりあえずOK。
(おそらく、iOS 9で画面を分割して使えるようになったのと関係するのだと思うのだけど、ちゃんと調べてない)

今日はここまで!