如何解决变量未在 Swift 中更新
我的 SwiftUI 应用程序有一个解析 JSON 数据的类。
正如您从 print
附近的评论中看到的那样,我不明白 print("return: " + self.productName)
行打印的变量仍然是空的(基本上是初始化的)。
为什么变量在 if 块内部更新,而不在外部更新?
有什么想法吗? 谢谢。
class ProductDecoer: ObservableObject {
@Published var productName: String = ""
func checkBarCode(barcode: String) -> String {
let api = <url>
let barcode = barcode
let format = ".json"
let url = URL(string: api + barcode + format)
URLSession.shared.dataTask(with: url!) { [self] data,response,error in
if let data = data {
if let jsonString = String(data: data,encoding: .utf8) {
productName = self.getProductInfo(json: jsonString)
print("checkBarCode: " + self.productName) // --> print correctly
}
}
}.resume()
self.productName = productName
print("return: " + self.productName) // --> is empty
return self.productName
}
func getProductInfo(json: String) -> String {
let jsonData = json.data(using: .utf8)!
let productInfo: ProductInfo = try! JSONDecoder().decode(ProductInfo.self,from: jsonData)
self.productName = productInfo.product_name
print("AAA: " + self.productName)
print("Prodotto: " + productInfo.product_name)
productName = productInfo.product_name
print("CC: " + productName)
return productName
}
struct ProductInfo: Codable {
var code: String
var product_name: String
enum CodingKeys: String,CodingKey {
case code
case product
enum ProductCodingKeys: String,CodingKey {
case product_name
}
}
init(from decoder: Decoder) throws {
let rootContainer = try decoder.container(keyedBy: CodingKeys.self)
let productContainer = try rootContainer.nestedContainer(keyedBy: CodingKeys.ProductCodingKeys.self,forKey: .product)
code = try rootContainer.decode(String.self,forKey: .code)
product_name = try productContainer.decode(String.self,forKey: .product_name)
print("BB: " + product_name)
}
func encode(to encoder: Encoder) throws {
var rootContainer = encoder.container(keyedBy: CodingKeys.self)
var productContainer = rootContainer.nestedContainer(keyedBy: CodingKeys.ProductCodingKeys.self,forKey: .product)
try rootContainer.encode(code,forKey: .code)
try productContainer.encode(product_name,forKey: .product_name)
}
}
}
解决方法
URLSession.shared.dataTask
是一个异步函数。这意味着它独立执行并在未来不确定的时间内完成。
您在它后面的 { }
中提供给它的称为完成处理程序或回调函数 -- 这是 dataTask
将在执行后调用的代码完成,因此任何依赖于返回值的事情都需要在该闭包内发生。
很遗憾,这意味着您将无法直接从 String
函数返回 checkBarCode
。但是,您也可以为它创建一个回调函数:
func checkBarCode(barcode: String,completion: @escaping (String) -> Void) {
let api = "URL"
let barcode = barcode
let format = ".json"
let url = URL(string: api + barcode + format)
URLSession.shared.dataTask(with: url!) { [self] data,response,error in
if let data = data {
if let jsonString = String(data: data,encoding: .utf8) {
self.productName = self.getProductInfo(json: jsonString)
print("checkBarCode: " + self.productName)
completion(self.productName)
}
}
}.resume()
}
可以这样调用:
checkBarCode(barcode: "") { productName in
//do something that depends on the value
}
,
您的 URLSession.shared.dataTask(with: url!) ...
是一个异步任务。这意味着您在 [self] data,error
中传递的闭包将在发出 URL 请求后被调用。因此,下面的 print
语句将在 URL 请求完成之前运行,这就是它为空的原因。
闭包内的 print
具有正确的值,因为它在那个时候已经被获取。
由于您的 ProductDecoer
类符合 ObservableObject
并且属性 productName
是使用 @Publisher 发布的,因此您不需要从函数 checkBarCode
返回任何内容,因此更改函数签名
func checkBarCode(barcode: String) { ... }
并删除其中 .resume()
之后的所有内容。
然后在您的 SwiftUI 视图中为该类创建一个属性并使用 @ObservedObject 属性
@ObservedObject var decoder = ProductDecoer()
下面是一个完整但简单的视图示例,当按下按钮时,它将请求文本字段中给出的条形码的产品名称。下载信息后,将设置已发布的属性 productName
,并且视图将显示它。
struct ContentView: View {
@ObservedObject var decoder = ProductDecoer()
@State private var barCode: String = ""
var body: some View {
VStack(alignment: .leading) {
TextField("Bar code",text: $barCode)
Text(decoder.productName)
Button {
decoder.checkBarCode(barcode: barCode)
}
label: {
Text("Load")
}
}
.padding()
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。