如何使用转义闭包和异步调用加载 UI?
How to load UI with escaping closures and async calls?
我编写了一个名为 'configureLabels()' 的函数,它应该发出 'GET' 请求并检索一个值,然后将其设置为标签的文本。该请求是异步的,所以我想我可以在请求完成时使用转义闭包来更新 UI 。我对编码比较陌生,所以我不确定自己做错了什么。我非常感谢任何人帮助解决这个问题。
这是包含 'configureLabels()' 方法的代码:
import UIKit
导入 SwiftyJSON
class ItemDetailViewController: UIViewController {
@IBOutlet weak var numberOfFiberGrams: UILabel!
var ndbnoResults = [JSON]()
var ndbno = ""
let requestManager = RequestManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
configureLabels()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func configureLabels() {
requestManager.ndbnoRequest(ndbno: ndbno) { (results) in
let json = JSON(results)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
这里是包含 'configureLabels()' 调用的函数的代码:
func ndbnoRequest(ndbno: String, apiKey: String, completionHandler: @escaping (_ results: JSON?) -> Void) {
Alamofire.request("https://api.nal.usda.gov/ndb/V2/reports?ndbno=\(ndbno)&type=f&format=json&api_key=\(apiKey)", method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
completionHandler(json)
print("successful ndbno request")
case .failure(let error):
completionHandler(nil)
print(error)
}
}
}
你的代码看起来不错,我发现你的代码唯一的问题是你没有在 failure
部分调用 completionHandler
,你需要始终调用完成块,这样它会给你想法你是否得到回应,因为你的 completionHandler
参数是 [JSON]
类型,因为你在 failure
部分没有回应,你没有在其中调用 completionHandler
。在 failure
.
的情况下,您可以做的是将其设置为 optional
并使用 nil
参数调用 completionHandler
func ndbnoRequest(ndbno: String, completionHandler: @escaping (_ results: [JSON]?) -> Void) {
let parameters = ["api_key": "tIgopGnvNSP7YJOQ17lGVwazeYI1TVhXNBA2Et9W", "format": "json", "ndbno": "\(ndbno)"]
Alamofire.request("https://api.nal.usda.gov/ndb/reports/V2", method: .get, parameters: parameters).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let ndbnoResults = json["foods"].arrayValue
completionHandler(ndbnoResults)
print("successful ndbno request")
case .failure(let error):
completionHandler(nil)
print("error with ndbno request")
}
}
}
现在以这种方式调用它,并将可选的包装在完成块中,这样您就可以确认您得到了响应。
requestManager.ndbnoRequest(ndbno: ndbno) { (results) in
if let result = results {
let json = JSON(result)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
else {
print("Problem to get response")
}
}
所有与 UI 相关的事情都必须 总是 在主线程上完成。
所以试试这个:
DispatchQueue.main.async {
let json = JSON(results)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
P.S。我同意 Nirav 关于 失败回调 的观点 - 你也应该处理它。并且我强烈建议您给函数和变量起更易读和更有意义的名称,而不是 "ndbnoRequest" 和 "ndbno"。几周后你就不会记得它是什么意思了:)
我编写了一个名为 'configureLabels()' 的函数,它应该发出 'GET' 请求并检索一个值,然后将其设置为标签的文本。该请求是异步的,所以我想我可以在请求完成时使用转义闭包来更新 UI 。我对编码比较陌生,所以我不确定自己做错了什么。我非常感谢任何人帮助解决这个问题。
这是包含 'configureLabels()' 方法的代码:
import UIKit
导入 SwiftyJSON
class ItemDetailViewController: UIViewController {
@IBOutlet weak var numberOfFiberGrams: UILabel!
var ndbnoResults = [JSON]()
var ndbno = ""
let requestManager = RequestManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
configureLabels()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func configureLabels() {
requestManager.ndbnoRequest(ndbno: ndbno) { (results) in
let json = JSON(results)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
这里是包含 'configureLabels()' 调用的函数的代码:
func ndbnoRequest(ndbno: String, apiKey: String, completionHandler: @escaping (_ results: JSON?) -> Void) {
Alamofire.request("https://api.nal.usda.gov/ndb/V2/reports?ndbno=\(ndbno)&type=f&format=json&api_key=\(apiKey)", method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
completionHandler(json)
print("successful ndbno request")
case .failure(let error):
completionHandler(nil)
print(error)
}
}
}
你的代码看起来不错,我发现你的代码唯一的问题是你没有在 failure
部分调用 completionHandler
,你需要始终调用完成块,这样它会给你想法你是否得到回应,因为你的 completionHandler
参数是 [JSON]
类型,因为你在 failure
部分没有回应,你没有在其中调用 completionHandler
。在 failure
.
optional
并使用 nil
参数调用 completionHandler
func ndbnoRequest(ndbno: String, completionHandler: @escaping (_ results: [JSON]?) -> Void) {
let parameters = ["api_key": "tIgopGnvNSP7YJOQ17lGVwazeYI1TVhXNBA2Et9W", "format": "json", "ndbno": "\(ndbno)"]
Alamofire.request("https://api.nal.usda.gov/ndb/reports/V2", method: .get, parameters: parameters).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let ndbnoResults = json["foods"].arrayValue
completionHandler(ndbnoResults)
print("successful ndbno request")
case .failure(let error):
completionHandler(nil)
print("error with ndbno request")
}
}
}
现在以这种方式调用它,并将可选的包装在完成块中,这样您就可以确认您得到了响应。
requestManager.ndbnoRequest(ndbno: ndbno) { (results) in
if let result = results {
let json = JSON(result)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
else {
print("Problem to get response")
}
}
所有与 UI 相关的事情都必须 总是 在主线程上完成。 所以试试这个:
DispatchQueue.main.async {
let json = JSON(results)
let fiber = json["food"]["nutrients"][7].dictionaryValue
for (key, value) in fiber {
if key == "value" {
self.numberOfFiberGrams.text = "\(value.stringValue)"
} else {
self.numberOfFiberGrams.text = "Fail"
}
}
}
P.S。我同意 Nirav 关于 失败回调 的观点 - 你也应该处理它。并且我强烈建议您给函数和变量起更易读和更有意义的名称,而不是 "ndbnoRequest" 和 "ndbno"。几周后你就不会记得它是什么意思了:)