![](/img/trans.png)
[英]How can I use async/await with SwiftUI in Swift 5.5?
[英]How do I use async / await in swift?
概述:我正在对要与 mapkit 一起使用的用户输入地址进行地理编码,但我无法使用 async / await 来管理延迟。
我有一个带有文本字段的 SwiftUI 视图,该视图调用一个函数 onSubmit 来启动地理编码过程......
TextField("Address ...", text: $searchString)
.onSubmit {
Task {
await switchToMap(address: searchString)
}
}
我的 switchToMap 函数...
func switchToMap(address: String) async {
Task {
locationManager.convertAddress(address: address)
}
if let coordinates = locationManager.addressCoordinates {
locationManager.jumpToPoint(location: coordinates)
}else{
print("Something went wrong")
}
}
LocationManager 功能(如相关)
func getCoordinates(addressString: String, completionHandler: @escaping(CLLocationCoordinate2D, NSError?) -> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(addressString) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?[0] {
let location = placemark.location!
completionHandler(location.coordinate, nil)
return
}
print(error?.localizedDescription ?? "")
completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
}
}
}
func convertAddress(address: String){
getCoordinates(addressString: address) { (location, error) in
if error != nil {
// Error Handler
print("Error converting address: \(error?.localizedDescription ?? "")")
return
}
DispatchQueue.main.async {
self.addressCoordinates = location
//self.jumpToPoint(location: location)
}
}
}
那么,我怎样才能利用 async / await 来完成所有这些工作呢? 我完全希望这段代码有几个问题。
让我们首先考虑CLGeocoder
方法geocodeAddressString
。 它使用传统的完成处理程序模式。 要在async
方法中使用它,我们需要将其包装在withCheckedThrowingContinuation
中:
let geocoder = CLGeocoder()
func placemarks(for string: String) async throws -> [CLPlacemark] {
try await withCheckedThrowingContinuation { continuation in
geocoder.cancelGeocode() // cancel prior one if any
geocoder.geocodeAddressString(string) { placemarks, error in
guard
error == nil,
let placemarks = placemarks?.filter( { $0.location != nil }),
!placemarks.isEmpty
else {
continuation.resume(throwing: error ?? CLError(.geocodeFoundNoResult))
return
}
continuation.resume(returning: placemarks)
}
}
}
请注意,我将CLGeocoder
从方法中取出,而是将其设为属性,以便我可以实现“取消先前请求(如果有)”逻辑。
另请注意,我只是获取CLPlacemark
的数组而不是坐标,但是如果您想要第一个的坐标,则可以执行以下操作:
func coordinate(for string: String) async throws -> CLLocationCoordinate2D {
let placemark = try await placemarks(for: string)
.first { $0.location != nil }
guard let coordinate = placemark?.location?.coordinate else {
throw CLError(.geocodeFoundNoResult)
}
return coordinate
}
展示了如何将基于完成的方法包装在一个延续中,我们应该注意到CLGeocoder
允许取消,但上述 placemarks placemarks(for:)
实现不会响应取消。 为此,我们需要将上述内容包装在withTaskCancellationHandler
中:
func placemarks(for string: String) async throws -> [CLPlacemark] {
try await withTaskCancellationHandler {
geocoder.cancelGeocode()
} operation: {
try await withCheckedThrowingContinuation { continuation in
geocoder.cancelGeocode() // cancel prior one if any
geocoder.geocodeAddressString(string) { placemarks, error in
guard
error == nil,
let placemarks = placemarks,
!placemarks.isEmpty
else {
continuation.resume(throwing: error ?? CLError(.geocodeFoundNoResult))
return
}
continuation.resume(returning: placemarks)
}
}
}
}
此时您可能不需要取消,但这很简单,并且您可能应该为任何可取消的过程添加其中之一。
有关重构旧代码以采用 Swift 并发的更多信息,请参阅 WWDC 2021 视频Swift 并发:更新示例应用程序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.