Home > Software design >  swift revise function to return value
swift revise function to return value

Time:12-27

I have a function locationManager that prints out zipCode but everytime I change this function to return a String or implement a completion block, the postal code no longer gets assigned a value. Can someone show how to make this function return a zipCode. I think I have to use async or Concurrency not sure though

import CoreLocation
import CoreLocationUI

class locationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    @Published var location: CLLocationCoordinate2D?
    let manager = CLLocationManager()
    let geocoder = CLGeocoder()
    override init() {
        super.init()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        manager.startUpdatingLocation()
    }
    func requestLocation() {
        manager.requestAlwaysAuthorization()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
            if error == nil {
                if let placemark = placemarks?.first {
                    if let postalCode = placemark.postalCode {
                        print(postalCode)
                    }
                }
            }
        }
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Error getting location: \(error)")
    }
    
    func status(){
        switch manager.authorizationStatus {
            case .authorizedWhenInUse:
                print("")
            case .authorizedAlways:
                locationManager(CLLocationManager(), didUpdateLocations: [CLLocation()])
            case .denied:
                print("")
            case .notDetermined:
                print("")
            case .restricted:
                print("")
        @unknown default:
            print("")
        }
    }
}

CodePudding user response:

You can't change a delegate method's signature. This is why your attempt to change didUpdateLocations to return a value or have a completion handler results in it not working.

If you want to provide a way for a user of your locationManager class (please rename the class to LocationManager - class names should start with uppercase letters) then you should add a completion property or add a completion parameter to the requestLocation function. Then your didUpdateLocations can call that completion handler passing back the zip code.

Also note that a lot of your location manager code is wrong and needs to be fixed. You should never be attempting to directly call the delegate method yourself. See the following answer for a full example of properly setting up and using CLLocationManager to get a user's current location. I've adapted that code to fit your use case.

The following code assumes the user of your LocationManager class will call requestLocation each time it wants to get the zip code of the user's current location.

import CoreLocation

class LocationManager: NSObject {
    private var manager: CLLocationManager?
    private var completion: ((String?) -> Void)?

    override init() {
        super.init()
    }

    func requestLocation(completion: @escaping (String?) -> Void) {
        self.completion = completion

        manager = CLLocationManager()
        manager?.delegate = self
        manager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
    }
}

extension LocationManager : CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // Only handle location that is within the desired 10 meters
        if let location = locations.first(where: { $0.horizontalAccuracy <= manager.desiredAccuracy }) {
            manager.stopUpdatingLocation()
            let geocoder = CLGeocoder()
            geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
                var postalCode: String?
                if error == nil {
                    if let placemark = placemarks?.first {
                        postalCode = placemark.postalCode
                    }
                }

                self.completion?(postalCode)
                self.completion = nil
                self.manager = nil
            }
        }
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Error getting location: \(error)")
    }

    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedWhenInUse, .authorizedAlways:
            manager.startUpdatingLocation()
        case .denied:
            print("Denied")
        case .notDetermined:
            manager.requestWhenInUseAuthorization()
        case .restricted:
            print("Restriced")
        @unknown default:
            print("Unexpected")
        }
    }
}

Sample usage:

let manager = LocationManager()
manager.requestLocation() { zipCode in
    if let zipCode {
        print("We got a zip code: \(zipCode)")
    }
}
  • Related