With coordinates from the locationManager I would like to show a map with the location (address) at the bottom obtained through reverse geoLocation. The coordinates and map are displaying correctly, but I am having trouble generating the address. I am trying to follow the example code at Find city name and country from latitude and longitude in Swift in the section iOS 11 or later. The two extensions (CLPlacemark and CLLocation) shown in the example are identical to what I am using. So it appears that although I am following the example usage I am not handling the asynchronous Placemark function correctly. The function getLocation() is correctly displaying the address, but it is not getting back to saveButton().
Any help with the asynchronous function return will be appreciated.
struct EntryView: View {
@Environment(\.managedObjectContext) var viewContext // core data
@ObservedObject private var lm = LocationManager() // location
@State private var entryLat: Double = 0.0
@State private var entryLong: Double = 0.0
@State private var addr: String = ""
var body: some View {
GeometryReader { g in
List {
Button(action: {
self.saveButton() // save entry button pressed
}) {
HStack {
Spacer()
Text ("Save")
Spacer()
}
}
}
.navigationBarHidden(true)
.navigationViewStyle(StackNavigationViewStyle())
}
// the save button has been pressed
func saveButton() {
// get coordinates and address
let addr = getLocation()
print("addr = \(addr)") // nothing displayed here except addr
// save entry to core data
let newEntry = CurrTrans(context: viewContext)
newEntry.id = UUID()
newEntry.entryDT = entryDT // entry date
newEntry.entryDsc = entryDsc // entry description
newEntry.moneyD = moneyD // money as double
newEntry.entryLat = entryLat // store location for maps
newEntry.entryLong = entryLong
newEntry.address = addr // formatted address
print("newEntry.address = \(newEntry.address ?? "")")
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
func getLocation() -> String {
// get location coordinates
let result = lm.getLocationCoordinates()
entryLat = result.0
entryLong = result.1
// get location address
let location = CLLocation(latitude: entryLat, longitude: entryLong)
location.placemark { placemark, error in
guard let placemark = placemark else {
print("Error:", error ?? "nil")
return
}
print("formatted address: \(placemark.postalAddressFormatted ?? "")")
addr = placemark.postalAddressFormatted ?? "Unknown"
return addr
}
}
}
The code below is part of the location manager.
extension CLLocation {
func placemark(completion: @escaping (_ placemark: CLPlacemark?, _ error: Error?) -> ()) {
CLGeocoder().reverseGeocodeLocation(self) { completion($0?.first, $1) }
}
}
extension CLPlacemark {
/// street name, eg. Infinite Loop
var streetName: String? { thoroughfare }
/// // eg. 1
var streetNumber: String? { subThoroughfare }
/// city, eg. Cupertino
var city: String? { locality }
/// neighborhood, common name, eg. Mission District
var neighborhood: String? { subLocality }
/// state, eg. CA
var state: String? { administrativeArea }
/// county, eg. Santa Clara
var county: String? { subAdministrativeArea }
/// zip code, eg. 95014
var zipCode: String? { postalCode }
/// postal address formatted
var postalAddressFormatted: String? {
guard let postalAddress = postalAddress else { return nil }
return CNPostalAddressFormatter().string(from: postalAddress)
}
}