I am working with a view that displays a list of locations. When a user taps on a location, a didSet
block containing a Task
is triggered in a separate class wrapped with the @ObservedObject
property:
struct LocationSearch: View {
@StateObject var locationService = LocationService()
@ObservedObject var networking: Networking
var savedLocations = SavedLocations()
var body: some View {
ForEach(locationService.searchResults, id: \.self) { location in
Button(location.title) {
getCoordinate(addressString: location.title) { coordinates, error in
if error == nil {
networking.lastLocation = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
// wait for networking.locationString to update
// this smells wrong
// how to better await Task completion in didSet?
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
savedLocations.all.append(networking.locationString!.locality!)
UserDefaults.standard.set(savedLocations.all, forKey: "savedLocations")
dismiss()
}
}
}
}
}
}
}
The Task
that gets triggered after networking.lastLocation
is set is as follows:
class Networking: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published public var lastLocation: CLLocation? {
didSet {
Task {
getLocationString() { placemark in
self.locationString = placemark
}
getMainWeather(self.lastLocation?.coordinate.latitude ?? 0, self.lastLocation?.coordinate.longitude ?? 0)
getAQI(self.lastLocation?.coordinate.latitude ?? 0, self.lastLocation?.coordinate.longitude ?? 0)
locationManager.stopUpdatingLocation()
}
}
}
What is a better way to ensure that the task has had time to complete and that the new string will be saved to UserDefaults
versus freezing my application's UI for one second?
In case it isn't clear, if I don't wait for one second, instead of the new value of locationString
being saved to UserDefaults
, the former value is saved instead because the logic in the didSet
block hasn't had time to complete.