0

I have some code that generates map coordinates and reverse geolocation when an entry save button is clicked. The button click calls the function saveButton() and in turn getLocation(). The print statement at the bottom of getLocation() is correctly displaying the formatted address but is not making it back up to saveButton() where the address is saved to core data and prints the newEntry.address (nothing is displayed). I am not getting any errors or warnings so I am wondering if there is a problem with an optional.

So in summary, reverse geoLocation is working, it just isn't getting stored in core data.

The code below mainly shows only snippets related to location coordinates and address.

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 transaction 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)
    }
}
Galen Smith
  • 299
  • 2
  • 14
  • 1
    Your `placemark` function is *asynchronous*. Look up callback functions or completion handlers to see how to deal with asynchronous functions in Swift. You will not be able to use the result right away in the following lines of code as you're trying to do -- you have to wait for it to complete first. – jnpdx Aug 23 '21 at 21:38
  • I am following the example at https://stackoverflow.com/questions/44031257/find-city-name-and-country-from-latitude-and-longitude-in-swift. It looks like it works there. – Galen Smith Aug 25 '21 at 03:20

0 Answers0