Swift 3使用URLSession将JSON解析为UITableView

时间:2017-04-17 10:24:39

标签: json swift urlsession

我正在尝试使用URLSession解析JSON,而不使用Alamofire或其他任何东西。

我只想拿JSON并将其放入UITableView。

我正在尝试将我从学习如何使用Alamofire解析JSON的知识与我在谷歌上找到的内容拼凑起来。 youtube或Stack等上的许多答案都使用NS来处理所有内容..NSURL,NSDictionary等等。或者只是输入代码而不解释什么/为什么。

我认为我几乎在那里,但我需要帮助了解我还剩下什么。

SO。

我在plst中允许任意加载

在Swift文件中我有以下

class Potter {

private var _title: String!
private var _author: String!
private var _imageURL: String!

let POTTER_URL = "http://de-coding-test.s3.amazonaws.com/books.json"

var title: String {
  if _title == nil {
    _title = ""
  }
  return _title
}

var author: String {
  if _author == nil {
    _author = ""
  }
  return _author
}

var imageURL: String {
  if _imageURL == nil {
    _imageURL = ""
  }
  return _imageURL
}

  func downloadJSON() {


    let url = URL(string: POTTER_URL)
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in

      if error != nil {
        print("Error")

      } else {

        if let content = data {
          do {
            if let jDict = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, AnyObject> {

              if let title = jDict["title"] as? String {
                self._title = title.capitalized

              }

              if let author = jDict["author"] as? String {
                self._author = author.capitalized
              }

              if let imgURL = jDict["imageURL"] as? String {
                self._imageURL = imgURL
              }
            }
          }
          catch {  
          }
        }
      }
    }
    task.resume()
  }
}

在我的Main.Storyboard中,我添加了tableview并设置了所有UI,在我的ViewController中,我设置了tableview委托。

我创建了

的属性
var potters = [Potter]()

我现在被困在如何填充这个数组,以及如何设置正确的线程

3 个答案:

答案 0 :(得分:2)

  1. Web服务返回一组对象:[Dictionary<String, AnyObject>]

  2. 如果您使用字典作为参数创建init方法,将会更容易。

  3. downloadJSON是异步任务,使用completionHandler是最好的方法。如果您想将downloadJSON放在Potter课程中,则应该是static函数。

  4. 最后,你应该像这样处理结果:

    Potter.downloadJSON { potters in
    
        self.potters = potters
    
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    
  5. 最终代码:

    class ViewController: UIViewController {
    
        var potters = [Potter]()
    
        @IBOutlet weak var tableView: UITableView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            Potter.downloadJSON { potters in
    
                self.potters = potters
    
                DispatchQueue.main.async {
    
                    self.tableView.reloadData()
                }
            }
        }
    }
    
    extension ViewController: UITableViewDelegate, UITableViewDataSource {
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return potters.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
    
            let potter = potters[indexPath.row]
            cell.textLabel?.text = potter.title
            cell.detailTextLabel?.text = potter.author
    
            return cell
        }
    }
    
    class Potter {
    
        private var _title: String!
        private var _author: String!
        private var _imageURL: String!
    
        static let POTTER_URL = "http://de-coding-test.s3.amazonaws.com/books.json"
    
        var title: String {
            if _title == nil {
                _title = ""
            }
            return _title
        }
    
        var author: String {
            if _author == nil {
                _author = ""
            }
            return _author
        }
    
        var imageURL: String {
            if _imageURL == nil {
                _imageURL = ""
            }
            return _imageURL
        }
    
        init(dict: Dictionary<String, AnyObject>) {
            self._title = dict["title"] as? String
            self._imageURL = dict["imageURL"] as? String
            self._author = dict["author"] as? String
        }
    
        class func downloadJSON(completion: @escaping (_ potters: [Potter]) -> Void) {
    
            let url = URL(string: POTTER_URL)
            let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
    
                if error != nil {
                    print("Error")
    
                } else {
    
                    if let content = data {
    
                        do {
                            if let jArray = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [Dictionary<String, AnyObject>] {
    
                                var potters = [Potter]()
                                for jDict in jArray {
                                    let potter = Potter(dict: jDict)
                                    potters.append(potter)
                                }
                                completion(potters)
                            }
                        }
                        catch {
                        }
                    }
                }
            }
            task.resume()
        }
    }
    

    enter image description here

答案 1 :(得分:2)

首先你的模型疯狂非常奇怪。

在Swift中从不使用支持的私有变量来获取只读属性。并且从不将属性声明为隐式展开的可选项,因为您懒得编写初始化程序。

整个模型可以简化为

class Potter {

    let title, author, imageURL: String

    init(title: String, author: String, imageURL : String) {
        self.title = title
        self.author = author
        self.imageURL = imageURL
    }
}

如果你使用struct,它甚至可以

struct Potter {
    let title, author, imageURL: String
}

因为您免费获得成员初始化程序。

其次,将方法downloadJSON()放出模型并将其放入控制器并在viewDidLoad()中调用它。

在控制器中声明下载URL和数据源数组

let POTTER_URL = "http://de-coding-test.s3.amazonaws.com/books.json"

var books = [Potter]()

您的方法downloadJSON()无法工作,因为JSON对象是一个数组([]),而不是字典({})。您需要一个循环来遍历项目,获取值,分别创建Potter项并将其附加到数据源。如果值不存在,则分配空字符串。最后在主线程上重新加载表视图。

func downloadJSON() {

    let url = URL(string: POTTER_URL)
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in

        if error != nil {
            print("DataTask error", error!)

        } else {
            do {
                if let bookData = try JSONSerialization.jsonObject(with: data!) as? [[String:String]] {
                    books.removeAll() // clear data source array
                    for book in bookData {
                        let title = book["title"] ?? ""
                        let author = book["author"] ?? ""
                        let imgURL = book["imageURL"] ?? ""
                        books.append(Potter(title: title, author: author, imageURL: imgURL))
                    }
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
                }
            }
            catch {
                print("Serialization error", error)
            }
        }

    }
    task.resume()
}

两个注释:

  • Swift 3中的标准JSON字典是[String:Any],在这种特殊情况下它甚至是[String:String]
  • .mutableContainers如果容器只在Swift中被读取且无用,则无用,因为该对象无法转换为NSMutableArray / -Dictionary并且您使用var iable免费获得可变性。

答案 2 :(得分:1)

方法downloadJSON()应该在ViewController中实现,因为它返回了Potter数据的数组。然后在URLSession响应中,您应该创建一个数组,该数组将充当tableview数据源。 (即self.arrTableData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String : AnyObject]]

然后进入tableView

func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int {

        return self.arrTableData.count
}

并在索引路径的行的单元格中

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   //create `potters` object with the value and use it else you can direcly use the value of objects as below.
     let dictPotters = self.arrTableData[indexPath.row]
      let title = dictPotters["title"]
  }

由于