我有一个这样的按钮
Button(action: {
// do something
}, label: {
Text("The price is \(price)")
})
第一个问题是按钮的标签部分需要一个视图。
此外,价格是使用类似
的方式从应用程序商店异步检索的func priceFor(_ productID: String,
onConclusion:GetPriceHanler?) {
inapp?.fetchProduct(productIdentifiers: [productID],
handler: { (result) in
switch result {
case .success(let products):
let products = products
guard let product = products.first
else {
onConclusion?("")
return
}
onConclusion?("\(product.price)")
case .failure(let error):
print(error.localizedDescription)
onConclusion?("")
}
})
}
因此,该函数在完成后会运行一个闭包。
我考虑过创建另一个函数,它返回一个视图来调用这个函数......类似
@ViewBuilder
func priceViewFor(_ productID: String) -> some View { }
如果函数返回价格或无效则为空,我没问题,但如果我这样做,我将在一个闭包内结束,从那里我无法返回视图。
类似的东西
@ViewBuilder
func priceViewFor(_ productID: String) -> some View {
myAppStore.priceFor(productID) { price in
return Text(price) ?????????????
}
我该如何做到这一点?
基本思想是有一个
@State
代表价格,无论是否已被获取。在 label:
视图生成器中检查此状态,如果未获取,则显示其他内容。
一个简单的设计是使用可选的:
@State var price: String?
Button {
// do something
} label: {
// check if the price has been fetched
if let price {
Text("The price is \(price)")
} else {
ProgressView("Loading Price...")
}
}
.disabled(price == nil) // you might want to disable the button before the price has been fetched
.onAppear {
// set the state in the completion handler
myAppStore.priceFor(productID) { price in
self.price = price
}
}
看到价格获取可能会失败,我建议使用
Result
代替:
@State var price: Result<String, Error>?
switch price {
case .none:
ProgressView("Loading Price...")
case .failure(let error):
Label("Failed! (\(error))", systemImage: "xmark")
case .success(let price):
Text("The price is \(price)")
}
这意味着您需要更改
priceFor
,以便将 Result<String, Error>
传递给 onConclusion
闭包。
此外,请考虑使用 Swift Concurrency API 进行重写。用法如下:
.task {
do {
price = .success(try await myAppStore.priceFor(productID))
} catch {
price = .failure(error)
}
}