28534


Swift2.2からSwift3.0にソースコードの移行を取り組んでいます。

JSONデータを取得して表示するアプリを作成しています。

[code]

    for item in items as! NSArray {
    
[/code]

上記の該当ソースコードでフィールドitemが複数要素となっていましてその複数要素を定数itemsとしてNSArrayにキャストして1要素を定数itemに取り出しています。以前のSwift2.2では正しく動作していたのですが、Swift3.0ではNSArrayにキャストがうまく動作せずに修正方法に苦慮しております。

アドバイスをいただけないでしょうか？

**[Swift3.0に変換したソースコード]**

[code]

    var okashiList :[(maker:String, name:String, link:String, image:String)] = []
    
    func searchOkashi(keyword : String){
    
        let keyword_encode = keyword.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
    
        let session = URLSession(configuration: URLSessionConfiguration.default, delegate:nil, delegateQueue:OperationQueue.main)
    
        let task = session.dataTask(with: URLRequest(url: Foundation.URL(string: "http://www.sysbird.jp/toriko/api/?apikey=guest&format=json&keyword=\(keyword_encode)&max=10&order=r")!), completionHandler: {
            (data, response, error) in
    
            do {
    
                let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
    
                if let items = json.object(forKey: "item") {
    
                    for item in items as! NSArray {
    
                        // コンパイルエラーが発生
                        guard let maker = item.object(forKey: "maker") else {
                            continue
                        }
    
                        // コンパイルエラーが発生
                        guard let name = item.object(forKey: "name") else {
                            continue
                        }
    
                        // コンパイルエラーが発生
                        guard let link = item.object(forKey: "url") else {
                            continue
                        }
    
                        // コンパイルエラーが発生
                        guard let image = item.object(forKey: "image") else {
                            continue
                        }
    
                        let data = (maker as! String,name as! String,link as! String,image as! String)
    
                        self.okashiList.append(data)
    
                    }
                }
    
            } catch {
                // エラー
            }
    
        })
        task.resume()
    }
    
[/code]


CC BY-SA 3.0
2016-08-28T02:40:51.510
28533
2016-08-30T22:44:51.920
2016-08-30T16:45:26.237
5343
5343
question
swift ios json swift3
Swift3.0におけるjsonパースについて


簡単に言うと、`NSDictionary`,
`NSArray`の値型がSwift3になって`AnyObject`から`Any`に変更されたので、取り扱いが難しくなっています。(Swift2の時点でも、それらをSwiftのDictionary型かArray型にキャストすることができたはずなので、そうしていれば移行はもう少し簡単だったのですが。)

対応としては`item.object(forKey: "image")`のような記述を片っ端から`(item as!
NSDictionary).object(forKey: "image")`か`(item as AnyObject).object(forKey:
"image")`(後者がXcodeのサジェスチョン)のように書き換えてしまえば良いのですが(そういえば`objectForKey(_:)`もSwift2の時代から使う必要はなく`item["image"]`のように書けたはずです)、今後のことも考えると、できるだけSwift的な書き方をしておいた方が移行は楽になるでしょう。

`do {}`の内側だけを書き直してみるとこんな形になります。

[code]

                // 受け取ったJSONデータをパース（解析）して格納します。
                let json = try JSONSerialization.jsonObject(with: data!) as! [String: AnyObject]
    
                self.okashiList.removeAll()
    
                if let items = json["item"] as? [[String: AnyObject]] {
    
                    for item in items {
    
                        guard let maker = item["maker"] as? String else {
                            continue
                        }
    
                        guard let name = item["name"] as? String else {
                            continue
                        }
    
                        guard let link = item["url"] as? String else {
                            continue
                        }
    
                        guard let image = item["image"] as? String else {
                            continue
                        }
    
                        let okashi = (maker,name,link,image)
    
                        self.okashiList.append(okashi)
                    }
                    //TableViewを更新する
                    self.tableview.reloadData()
                }
    
[/code]

`json["item"] as? [[String: AnyObject]]`の部分は元のコードを見る限り`json["item"] as?
[[String:
String]]`として良いように思えるのですが、使っていないフィールドにString以外のものが含まれているとうまく動かない可能性があるので、上記のようにしておきました。  
(これに限らず、元のJSONデータのサンプルを示しておいた方が、回答が書きやすくなるので、できればJSONデータのテキスト表現をご質問中に記載してもらった方が良いですね。上記のコードもまだ何か不備があるかもしれませんが、その際はコメント等でお知らせください。)

お試しください。

* * *

(補足)

  * `[String: AnyObject]`の部分は`[String: Any]`の方が(Swift3の方向性的には)良いような気もするのですが、Objective-Cの`id`型を`Any`としてimportする動作は最近導入されたばかりで、まだ安定していないような気がしたので、とりあえず`AnyObject`にしてあります。(簡単なテストデータで見る限りどちらでも動くようです。)

  * `.allowFragments`の指定は、結果を`NSDictionary`や`[String: AnyObject]`にキャストするならば無い方が良いので削除しました。単独の文字列や数値といったJSONとしては不正なデータが返ってきた時に、`as!`によるアプリのクラッシュではなく、`JSONSerialization`のerrorとしてキャッチできるようになります。


CC BY-SA 3.0
2016-08-28T03:16:52.740
28534
2016-08-30T22:44:51.920
2016-08-30T22:44:51.920
13972
13972
28533
answer