swiftdelegateslocation-services

delegate return nil after calling it in a class


I'm using this class to get the location and send it with a delegate to another view controller, but the delegate is returning nil when called. my class:

protocol LocationUpdateProtocol {
    func locationDidUpdateToLocation(location : CLLocation)
}
/// Notification on update of location. UserInfo contains CLLocation for key "location"
let kLocationDidChangeNotification = "LocationDidChangeNotification"
class LocationServiceHelper:NSObject,CLLocationManagerDelegate {

    static let sharedManager = LocationServiceHelper()
    private let locationManager = CLLocationManager()
    var currentLocation:CLLocation?
    weak var delegate :LocationUpdateProtocol?
    private override init() {
        super.init()
        self.locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()
    }

    // MARK: CLLocationManagerDelegate

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let delegate = self.delegate else {
            return
        }
        currentLocation = locations.first
        let userInfo:NSDictionary = ["location":currentLocation!]
        delegate.locationDidUpdateToLocation(location: currentLocation!)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: kLocationDidChangeNotification), object: self, userInfo: userInfo as? [AnyHashable : Any])
    }
}

I called the delegate in another class below:

var locationServiceReference = LocationServiceHelper.sharedManager
    override func viewDidLoad() {
        super.viewDidLoad()
        fillFields()
      locationServiceReference.delegate = self
        self.mapView.showsUserLocation = true
    }

func locationDidUpdateToLocation(location: CLLocation) {
       // print(location)
        // let myLocation = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
        let myLocationForTesting = CLLocationCoordinate2D(latitude: 33.410165, longitude: 35.480060)
        myLocation = location
            let region = MKCoordinateRegion(center: myLocationForTesting, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
        self.mapView.setRegion(region, animated: true)

    }

in the did update location , the delegate is returning nil, any idea what's happening? the function isn't been called, since the delegate is returning nil.


Solution

    1. Make sure that your controller still in memory
    2. You don't need to use guard. Optional chaining works better in such cases:
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        currentLocation = locations.first
        let userInfo:NSDictionary = ["location":currentLocation!]
        delegate?.locationDidUpdateToLocation(location: currentLocation!)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: kLocationDidChangeNotification), object: self, userInfo: userInfo as? [AnyHashable : Any])
    }
    
    1. Make sure that func locationManager(_:didUpdateLocations) called after delegate setted.
    2. For future you want to pass last location when delegate changes as:
    weak var delegate :LocationUpdateProtocol? {
        didSet { 
            guard let last = currentLocation else { return }
            delegate?.locationDidUpdateToLocation(location: last)
        }
    }
    
    1. Since it's singleton with only 1 delegate, make sure that you not setting delegate to nil or another short living obejct somewhere else