Help us understand the problem. What is going on with this article?

Swift2.0への移行メモ

More than 3 years have passed since last update.
  • throwsするメソッドとnil guardの記法

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

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重カッコ)を避けることができる
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()

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

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 アプリの構造がどのようになっているか紐解いてみる

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
例えば、UIControlEvents構造体のメンバを列挙してターゲットアクションに応答するイベントを指定する際に、addTargetメソッドの引数の記述法がswift1.2とswift2.0で変更されている。
swift2.0では配列に構造体のメンバを列挙する。2015/07/16現在、iOS9.0のAPI ReferenceのaddTarget:action:forControlEvents:の欄には記載無。

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

externvoid@github
会社を5社渡り歩いて、今はフリーランスのエンジニアです。大阪近郊の都市に在住。 鉄鋼、電子部品、バイオ関連、派遣プログラマー、中小製造業の会社を経て今は一休み中。 1万人を超える会社から、10人以下の会社まで経験できました。 仕事で、電磁界物理シミュレーター、デジカメファーム、Docomo FOMA通信監視、社内特許出願経過閲覧Webシステムとか作ってきました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away