繁体   English   中英

如何在 swift 中使用 async / await?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM