如何在Swift中正确更新@Published变量?

sr4lhrrt  于 2023-01-16  发布在  Swift
关注(0)|答案(2)|浏览(145)
    • 背景**

我正在尝试构建一个类,它可以轻松地将字符串(如地址)转换为CLLocationCoordinate2D,以备将来使用,并将其保存到数据库中。
我有一个类似下面的类:

final class PublishMapData: ObservableObject {
    
    @Published var userAddressLat: Double = 0.0
    @Published var userAddressLong: Double = 0.0
    
    func saveMapData(address: String){
        let address = "One Apple Park Way, Cupertino, CA 95014" //simulating the call from a save button for instance
        convertAddress(address: address)
        print(String(userAddressLat)) //this prints 0.0
        print(String(userAddressLong)) //this print 0.0
        //{...extra code here...}
        //this is where I would be storing the coordinates into something like Firestore for later use
    }
    
    
    func convertAddress(address: String) {
        getCoordinate(addressString: address) { (location, error) in
            if error != nil {
                
                return
            }
            DispatchQueue.main.async {
                self.userAddressLat = location.latitude
                print(self.userAddressLat) //this prints the correct value
                self.userAddressLong = location.longitude
                print(self.userAddressLong) //this prints the correct value
            }
        }
    }

    private func getCoordinate(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
                }
            }
                
            completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
        }
    }

}

由于某种原因,我无法从convertAddress函数中获取lat、long值,从而无法正确存储在@Published变量中。我做错了什么?我仍在学习Swift。提前感谢您的帮助。

kiayqfof

kiayqfof1#

根据https://developer.apple.com/documentation/corelocation/clgeocoder/1423509-geocodeaddressstring

geocodeAddressString(_:completionHandler:)

是一个异步函数,这意味着它的完成处理程序将在稍后的时间点执行,并且被调用的函数将立即返回。
所以当你打电话

convertAddress(address: address)

它立即返回,调度稍后调用的dispatchQueue闭包。

print(String(userAddressLat)) //this prints 0.0
print(String(userAddressLong))

接下来执行,打印0.0

DispatchQueue.main.async {
    self.userAddressLat = location.latitude
    print(self.userAddressLat) //this prints the correct value
    self.userAddressLong = location.longitude
    print(self.userAddressLong) //this prints the correct value
}

稍后执行。

wfypjpf4

wfypjpf42#

@lastbreath感谢您强调geocodeAddressString(_:completionHandler:)的异步特性,因此我发现可以使用异步geocodeAddressString调用来代替之前的方法。
这在您提供的链接中已注明:

func geocodeAddressString(_ addressString: String) async throws -> [CLPlacemark]

这是固定的代码......要实现将值发送到@Published变量要简单得多。

final class PublishMapData: ObservableObject {
    
    @Published var userAddressLat: Double = 0.0
    @Published var userAddressLong: Double = 0.0
    
    func saveMapData(address: String){
        Task {
            do {
                let address = "One Apple Park Way, Cupertino, CA 95014" //simulating the call from a save button for instance
                try await getCoordinate(addressString: address)
                print(String(userAddressLat)) //this now prints correct value
                print(String(userAddressLong)) //this now prints correct value
                //{...extra code here...}
                //this is where I would be storing the coordinates into something like Firestore for later use
            }
            catch {
                
            }
        }
    }
    
    func getCoordinate(addressString : String) async throws {
        let geocoder = CLGeocoder()
        let placemark = try await geocoder.geocodeAddressString(addressString)
        await MainActor.run(body: {
            userAddressLat = placemark[0].location!.coordinate.latitude
            userAddressLong = placemark[0].location!.coordinate.longitude
        })
    }
    

}

感谢您帮助我们找到答案。

相关问题