如何获取 json 数据以通过 segue 传递它
How to get json data for passing it by segue
我尝试获取 json 数据,而 URLSession.dataTask
是 运行 set segue 。(每个 json 数据作为发件人)
所以首先,我制作了自己的 Class 数组 productList = [Product]()
。
接下来,我调用 getJsonData()
并在其中设置 URLSession.dataTask
方法。所以我得到了 Parsed json 数据。但是,当我尝试从 dataTask completionHandler
保存 json 数据(将每个数据附加到 productList
)时,它无法正确保存。(结果 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
是空的。所以我不能将 prepareForSegue
与 productList[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.
}
}
}
您没有发布所有代码,但我认为您的错误在于您正在执行异步任务,然后立即对正在修改的数组调用 print。我不希望在任务完成之前填充数组。
您的 tableView 是否实际填充了结果?您是否打印出 JSON 以确保您的数据正确匹配?是否正在打印错误?
编辑:
要沿 segue 传递数据,您需要检索 destinationViewController
作为变量并将信息传递给它。有一种叫做 prepareForSegue
的方法可以让您在操作发生之前处理初始状态。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ExampleVC
vc.setProducts(productList)
}
类似的东西。显然改变你的 class 和变量名称
您需要实施 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]
}
}
确切的语法会有所不同(目标视图控制器的 class 名称是什么)并且您必须在目标中声明 product
属性,并且目的地的 viewDidLoad
需要使用那个 属性,但希望它能说明基本思想。
一些额外的观察:
我建议您检查 rawItem
并确保它是一个字典,其中包含一个名为 goods
的键,并且与该键关联的值确实是一个数组字典。没有看到你的 JSON,就不可能说出到底出了什么问题。
此外,考虑:
if let fineItem = rawItem["goods"] as? [[String:Any]] {
...
}
如果失败,您将永远不会知道。我可能会建议:
guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
print("goods not found or wrong type")
return
}
...
顺便说一句,与您手头的问题无关,直接在数据任务的完成处理程序中改变 productList
有点危险。不要从一个线程异步改变从另一个线程读取的数组。数组不是线程安全的。数据任务完成处理程序应该构建一个本地数组,只有当它完成时,在你将重新加载分派到主队列的地方,你应该插入代码以在 [=61 之前用你的本地数组替换 productList
=] 已重新加载。
此外,您目前正在解析循环中调用 reloadData
。您通常会在解析循环结束时调用它。现在,如果您的数据集有 100 行,您将重新加载 table 100 次。
对 data!
的引用有点危险。如果您没有互联网连接,data
将变为 nil
,您的代码将会崩溃。我建议:
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
然后您可以将 data!
引用替换为 data
。
我解决了我的问题,这是我的最终代码。
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]
}
}
}
}
我尝试获取 json 数据,而 URLSession.dataTask
是 运行 set segue 。(每个 json 数据作为发件人)
所以首先,我制作了自己的 Class 数组 productList = [Product]()
。
接下来,我调用 getJsonData()
并在其中设置 URLSession.dataTask
方法。所以我得到了 Parsed json 数据。但是,当我尝试从 dataTask completionHandler
保存 json 数据(将每个数据附加到 productList
)时,它无法正确保存。(结果 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
是空的。所以我不能将 prepareForSegue
与 productList[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.
}
}
}
您没有发布所有代码,但我认为您的错误在于您正在执行异步任务,然后立即对正在修改的数组调用 print。我不希望在任务完成之前填充数组。
您的 tableView 是否实际填充了结果?您是否打印出 JSON 以确保您的数据正确匹配?是否正在打印错误?
编辑:
要沿 segue 传递数据,您需要检索 destinationViewController
作为变量并将信息传递给它。有一种叫做 prepareForSegue
的方法可以让您在操作发生之前处理初始状态。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ExampleVC
vc.setProducts(productList)
}
类似的东西。显然改变你的 class 和变量名称
您需要实施 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]
}
}
确切的语法会有所不同(目标视图控制器的 class 名称是什么)并且您必须在目标中声明 product
属性,并且目的地的 viewDidLoad
需要使用那个 属性,但希望它能说明基本思想。
一些额外的观察:
我建议您检查
rawItem
并确保它是一个字典,其中包含一个名为goods
的键,并且与该键关联的值确实是一个数组字典。没有看到你的 JSON,就不可能说出到底出了什么问题。此外,考虑:
if let fineItem = rawItem["goods"] as? [[String:Any]] { ... }
如果失败,您将永远不会知道。我可能会建议:
guard let fineItem = rawItem["goods"] as? [[String:Any]] else { print("goods not found or wrong type") return } ...
顺便说一句,与您手头的问题无关,直接在数据任务的完成处理程序中改变
productList
有点危险。不要从一个线程异步改变从另一个线程读取的数组。数组不是线程安全的。数据任务完成处理程序应该构建一个本地数组,只有当它完成时,在你将重新加载分派到主队列的地方,你应该插入代码以在 [=61 之前用你的本地数组替换productList
=] 已重新加载。此外,您目前正在解析循环中调用
reloadData
。您通常会在解析循环结束时调用它。现在,如果您的数据集有 100 行,您将重新加载 table 100 次。对
data!
的引用有点危险。如果您没有互联网连接,data
将变为nil
,您的代码将会崩溃。我建议:guard let data = data, error == nil else { print("network request failed: error = \(error)") return }
然后您可以将
data!
引用替换为data
。
我解决了我的问题,这是我的最终代码。
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]
}
}
}
}