Swift2.0への移行メモ

- throwsするメソッドとnil guardの記法

以下のコードはrefreshボタンを押したら、newsUrlStringに入ってるURLへのセッションを開始し、受け取ったdataに入っているJSON形式のデータから、entriesキーを抜き出す処理をしている。

```swift:swift1.2.swift
                                                                              
@IBAction func refresh(sender: AnyObject) {
  let url = NSURL(string: newsUrlString)!
  
  let task = NSURLSession.sharedSession().dataTaskWithURL(url,
    completionHandler: { data, response, error in 
      // JSONデータを辞書に変換する
    let dict = NSJSONSerialization.JSONObjectWithData(data!,
      options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
    
    // /responseData/feed/entriesを取得する
    if let responseData = dict["responseData"] as? NSDictionary {
        if let feed = responseData["feed"] as? NSDictionary {
            if let entries = feed["entries"] as? NSArray {
                self.entries = entries
            }
        }
    }
        // メインスレッドにスイッチする
    dispatch_async(dispatch_get_main_queue(), {
        // テーブルビューを更新する
        self.tableView.reloadData()
    }) //in complitionHandler
  })
  task!.resume()
}  
```

Swift2.0になると、仕様が次の様に変わる。

- NSJSONSerializationがエラーをthrowsする様になる。
  - throwsするメソッドはdo {...} で囲む。
  - 直後にcatch{...}を置く。
- インスタンスのnilチェック作法が変更される。
  - nilでないなら、curly bracket{}が実行される(swift1.2)
  - nilでないなら、guard let else {retrun}の次が実行される(swift2.0)
  - swift2.0ではswift1.2で生じたcurly bracket{}の連鎖(if letの3重カッコ)を避けることができる

```swift:swift2.0.swift
@IBAction func refresh(sender: AnyObject) {                                     
  let url = NSURL(string: newsUrlString)!
  let task = NSURLSession.sharedSession().dataTaskWithURL(url,
    completionHandler: { data, response, error in
    do {
        // JSONデータを辞書に変換する
      let dict = try NSJSONSerialization.JSONObjectWithData(data!,
          options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
        
        // /responseData/feed/entriesを取得する
      guard let responseData = dict["responseData"] as? NSDictionary
                                                             else {return}
      guard let feed = responseData["feed"] as? NSDictionary else {return}
      guard let entries = feed["entries"] as? NSArray else {return}
      self.entries = entries
    } catch {}
    
      // メインスレッドにスイッチする
    dispatch_async(dispatch_get_main_queue(), {
          // テーブルビューを更新する
      self.tableView.reloadData()
    }) //in complitionHandler
  })
  task!.resume()
}
```
do {までは、swift1.2と同じだけど、エラーをthrowsするNSJSONSerializationのところと、受け取ったdataの扱いの部分が大きく異なる。

なお、refreshイベントハンドラの構造は、
```
1. let url ...
2. let task ...
3. task!.resume()
```

のこれだけ！２の部分が長いので複雑に見えるけど、下の様な構造になっている。

```
completionHandler: { ... in
do {
  let dict = try NSJSONSerialization.JSONObjectWithData( ... )
  ...
} catch { ... }
dispatch_asynce( ...

)
```

- complitionHanderラベルで与えているclosure部分を別途gと言う名前で定義してやるとこんな感じになる。closureの中ではViewControllerのインスタンスをselfで参照できないので、工夫が必要だった。
(こんなコンパイルエラー発生：Value of type 'NSObject -> () -> ViewController' has no member 'entries'、closure NSObject->Voidが参照しているViewController(self)にはentriesなんてメンバは無いよ！インスタンス生成前にself参照してるからlazy var g =とすれば良いのか？)
~~self.entries.addObjectsFromArray: NSObject型のインスタンスselfがentriesのメソッドを呼び出そうとしているのがコンパイラはおかしな記述だと判断しているらしい。~~

- class定義の中のメドッド定義の外ではselfは使えないみたい。

*参照したサイト:*  [iOS アプリの構造がどのようになっているか紐解いてみる](http://d.hatena.ne.jp/glass-_-onion/20120507/1336320038)

```swift:swift2.0.swift
@IBAction func refresh(sender: AnyObject) {                                     
  let url = NSURL(string: newsUrlString)!
  let task = NSURLSession.sharedSession().dataTaskWithURL(url,
    completionHandler: g)
  task!.resume()
}
let g = {(data: NSData?, response: NSURLResponse?, errro: NSError?) -> Void in
  // JSONデータを辞書に変換する
    let app : AppDelegate = UIApplication.sharedApplication().delegate
        as! AppDelegate
    let vc : ViewController =
      app.window!.rootViewController!.childViewControllers[0]
        as! ViewController
  do {
    let dict = try NSJSONSerialization.JSONObjectWithData(data!,
      options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
    // /responseData/feed/entriesを取得する
    guard let responseData = dict["responseData"] 
                              as? NSDictionary else {return}
    guard let feed = responseData["feed"] as? NSDictionary else {return}
    guard let entries = feed["entries"] as? NSArray else {return}
    vc.entries.addObjectsFromArray(entries as [AnyObject])//この処理はThread Safeで動いてるみたい。
    // vc.entries = entries //これ、危険
  } catch {}
  
  // メインスレッドにスイッチする
  dispatch_async(dispatch_get_main_queue(), {
    // テーブルビューを更新する
    vc.tableView.reloadData()
  })
} //end of closure
```

- 複数ビットマスクの記述

*参考：*[Swift 2/Xcode 7 beta - multiple bitmasks produce error](http://stackoverflow.com/questions/30835022/swift-2-xcode-7-beta-multiple-bitmasks-produce-error)
例えば、UIControlEvents構造体のメンバを列挙してターゲットアクションに応答するイベントを指定する際に、addTargetメソッドの引数の記述法がswift1.2とswift2.0で変更されている。
swift2.0では配列に構造体のメンバを列挙する。2015/07/16現在、iOS9.0のAPI ReferenceのaddTarget:action:forControlEvents:の欄には記載無。

```swift:swift1.2.swift
pallet.addTarget(self, action: "dragReport", forControlEvents:.TouchDragInside | .TouchDragOutside)
//could not find member TouchDragInside@swift2 compiler error
```

```swift:swift2.0.swift
pallet.addTarget(self, action: "dragReport", forControlEvents:[.TouchDragInside, .TouchDragOutside])

```