如何通过segue

时间:2017-03-26 14:16:45

标签: ios json swift urlsession

我试图在URLSession.dataTask运行时获取json数据来设置segue。(每个json数据作为发送者)

首先,我制作了自己的类数组productList = [Product]()。 接下来,我调用getJsonData()并在其中设置URLSession.dataTask方法。所以我得到了Parsed json数据。但是,当我尝试从productList保存该json数据(将每个数据附加到dataTask completionHandler)时,它无法正确保存。(结果productList[]

我想通过segue传递已解析的json数据。我怎样才能做到这一点?

已编辑 -

class MainVC: UITableViewController {

    var productList = [Product]()

    override func viewDidLoad() {
        super.viewDidLoad()

        getJsonData()

    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return productList.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
            let product = productList[indexPath.row]

            cell.configureCell(product)

            return cell
        } else {
            return UITableViewCell()
        }
    }


    func getJsonData() {
        let url = URL(string: "http://demo7367352.mockable.io")
        let request = URLRequest(url: url!)
        let defaultSession = URLSession(configuration: URLSessionConfiguration.default)


        let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in

            do {
                guard let data = data, error == nil else {
                    print("network request failed: error = \(error)")
                    return
                }

                guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                    print("error trying to convert data to JSON")
                    return
                }


                if let fineItem = rawItem["goods"] as? [[String:Any]] {

                    for item in fineItem {
                        let eachProduct = Product(title: "", price: 0)

                        let title = item["TITLE"]
                        let price = item["PRICE"]
                        let regDate = item["REGDATE"]
                        let description = item["DESCRIPTION"]
                        let iconURL = item["ICON_URL"]
                        let images = item["IMAGES"]


                        if let title = title as? String {
                            eachProduct.title = title
                        }
                        if let price = price as? String {
                            eachProduct.price = Int(price)!
                        }

                        DispatchQueue.main.async(execute: {
                            self.productList.append(eachProduct)
                            self.tableView.reloadData()
                        })
                    }

                }

            } catch  {
                print("error trying to convert data to JSON")
                return
            }

        })
        task.resume()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToProductDetail" {
            if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {

            }
        }
    }
 }

现在,我可以从URLSession DataTask解析数据。我想实现tableView的segue来显示细节。但是productList是空的。因此我无法将prepareForSegueproductList[indexPath.row]一起使用。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToProductDetail" {
        if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
            controller.product = productList[indexPath.row] // productList is nil.
        }
    }
}

3 个答案:

答案 0 :(得分:0)

您没有发布所有代码,但我相信您的错误在于您正在执行异步任务,然后立即在正在修改的阵列上调用print。我不希望在任务完成之前填充数组。

你的tableView实际上是否填充了结果?您是否打印出JSON以确保数据正确匹配?是否打印错误?

编辑: 要沿segue传递数据,您需要检索destinationViewController作为变量并将信息传递给它。有一种名为prepareForSegue的方法可以让您在行动发生之前处理初步状态。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   let vc = segue.destination as! ExampleVC
   vc.setProducts(productList)
}

这样的事情。显然改变你的类和变量名

答案 1 :(得分:0)

您需要实施prepare(for:sender:)并将数据传递到那里:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let controller = segue.destination as? MySecondViewController, indexPath = tableView.indexPathForSelectedRow {
        controller.product = productList[indexPath.row]
    }
}

确切的语法会有所不同(目标视图控制器的类名是什么),您必须在目标中声明product属性,并且目的地{{1需要使用该属性,但希望它能说明基本思想。

其他一些观察结果:

  1. 我建议您检查viewDidLoad并确保它是一个字典,其中包含一个名为rawItem的密钥,并且与该密钥关联的值实际上是一个数组的词典。如果没有看到你的JSON,就不可能说出到底出了什么问题。

    另外,请考虑:

    goods

    如果失败了,你永远不会知道。我可能会建议:

    if let fineItem = rawItem["goods"] as? [[String:Any]] {
        ...
    }
    
  2. BTW,与您手头的问题无关,直接在数据任务的完成处理程序中突变guard let fineItem = rawItem["goods"] as? [[String:Any]] else { print("goods not found or wrong type") return } ... 有点危险。不要异步改变从另一个线程读取的一个线程中的数组。数组不是线程安全的。数据任务完成处理程序应构建一个本地数组,并且只有在完成后,在您将重新加载到主队列的位置内,您应插入代码以将productList替换为您的本地重新加载表之前的数组。

  3. 此外,您当前正在解析循环中调用productList。您通常在解析循环结束时调用它。现在,如果您的数据集有100行,那么您将重新加载表格100次。

  4. reloadData的引用有点危险。如果您没有互联网连接,data!将为data,您的代码将崩溃。我建议:

    nil

    然后,您可以将guard let data = data, error == nil else { print("network request failed: error = \(error)") return } 引用替换为data!

答案 2 :(得分:0)

我解决了我的问题,这是我的最终代码。

class MainVC: UITableViewController {

    var productList = [Product]()

    override func viewDidLoad() {
        super.viewDidLoad()

        getJsonData()

    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return productList.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
            let product = productList[indexPath.row]

            cell.configureCell(product)

            return cell
        } else {
            return UITableViewCell()
        }
    }

    // parsing
    func getJsonData() {
        let url = URL(string: "http://demo7367352.mockable.io")
        let request = URLRequest(url: url!)
        let defaultSession = URLSession(configuration: URLSessionConfiguration.default)


        let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in

            do {
                guard let data = data, error == nil else {
                    print("network request failed: error = \(error)")
                    return
                }
                guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
                    print("error trying to convert data to JSON")
                    return
                }
                guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
                    print("goods not found or wrong type")
                    return
                }

                for item in fineItem {
                    let eachProduct = Product(title: "", price: 0)

                    let title = item["TITLE"]
                    let price = item["PRICE"]


                    if let title = title as? String {
                        eachProduct.title = title
                    }
                    if let price = price as? String {
                        eachProduct.price = Int(price)!
                    }

                    self.productList.append(eachProduct)
                }

                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })

            } catch  {
                print("error trying to convert data to JSON")
                return
            }

        })
        task.resume()
    }

    // segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToProductDetail" {
            if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
                controller.product = productList[indexPath.row]
            }
        }
    }
}