重构函数json解析器Swift

时间:2016-11-21 07:03:29

标签: json swift

我有一个用于解析json的函数,它工作正常,但不是我重复的事情,我正在寻找一种方法来重构它,以便我可以将我的任何模型对象传递给函数的闭包,它应该仍然以同样的方式行事。

这是我必须传递给有趣的对象列表

  1. 视频
  2. 事件
  3. 趋势
  4. 新闻
  5. 下面的函数将Video作为闭包的参数

    func fetchFeedForUrlString(urlString: String, completion: @escaping     ([Video]) -> ()){
    
        let url = NSURL(string: urlString)!
    
        URLSession.shared.dataTask(with: url as URL) { (data, responseUrl, error) -> Void in
            if error != nil{
                print("Unable to fetch data\(error)")
                return
            }
    
            if let wrappedData = data,
                let json = try? JSONSerialization.jsonObject(with: wrappedData, options: .allowFragments) as? [String: Any]{
    
                if let results = json?["data"] as? [[String: AnyObject]]{
                    let videos = results.map({return Video(dictionary: $0)})
    
                    DispatchQueue.main.async {
                        completion(videos)
                    }
                }
    
            }
    
        }.resume()
    }
    

1 个答案:

答案 0 :(得分:1)

实际上有点难以告诉你如何重构它,但首先我要说你应该宣布一个JSONDecodable协议,或者某种协议。通过这种方式,您可以定义描述可以通过JSON初始化的对象的协议。

protocol JSONDecodable {
    init?(_ json: [String: Any])
}

使Video类符合JSONDecodable协议

class Video: JSONDecodable {
    init?(_ json: [String: Any]) {
        // your init code here. It can return nil in case the JSON is invalid
    }
}

然后您可以使用Swift泛型来创建泛型函数

func fetchFeedForUrlString<T: JSONDecodable>(urlString: String, completion: @escaping     ([T]) -> ()){

    let url = NSURL(string: urlString)!

    URLSession.shared.dataTask(with: url as URL) { (data, responseUrl, error) -> Void in
        if error != nil{
            print("Unable to fetch data\(error)")
            return
        }

        if let wrappedData = data,
            let json = try? JSONSerialization.jsonObject(with: wrappedData, options: .allowFragments) as? [String: Any]{

            if let results = json?["data"] as? [[String: AnyObject]]{
                let videos = results.flatMap({return T($0)})

                DispatchQueue.main.async {
                    completion(videos)
                }
            }

        }

    }.resume()
}

要小心,因为这样你必须声明两个不同的函数来解析一组JSONDecodable个对象或只有一个。

要使用它,只需写下

即可
Instance.fetchFeedForUrlString(...) { (videos as [Video]) in
    /// your code here
}

编写(videos as [Video]),告诉编译器T参数是Video类型,否则无法在其他地方推断出类型。 这种实施方式并不是最好的,而且还可以改进。