Roggie
Roggie

Reputation: 1217

Why is this Singleton Location Manager class returning a nil Location?

I am trying to use the below LocationSingleton Class from this blog in my project. I like the simplicity of its usage.

You start updating location by simply calling:

LocationSingleton.sharedInstance.startUpdatingLocation()

Get the last location by simply calling:

LocationSingleton.sharedInstance.lastLocation

My intention is to start location services, get the last location so that I can then fetch users from Firebase using the location returned.

The issue is that if I call lastLocation straight after startUpdatingLocation it returns nil.

After some debugging I've found the reason is because location services are slow to start on the device and therefore when lastLocation is called the devices hasn't acquired the location yet. I would like to execute the next command as soon as the lastLocation has been recorded. How can I achieve that?

I would like to understand how the Protocol is being used?

import UIKit
import CoreLocation

protocol LocationServiceDelegate {
    func locationDidUpdateToLocation(currentLocation: CLLocation)
    func locationUpdateDidFailWithError(error: NSError)
}

class LocationSingleton: NSObject,CLLocationManagerDelegate {
    var locationManager: CLLocationManager?
    var lastLocation: CLLocation?
    var delegate: LocationServiceDelegate?

    static let sharedInstance:LocationSingleton = {
        let instance = LocationSingleton()
        return instance
    }()

    override init() {
        super.init()
        self.locationManager = CLLocationManager()

        guard let locationManagers=self.locationManager else {
            return
        }

        if CLLocationManager.authorizationStatus() == .notDetermined {
            //locationManagers.requestAlwaysAuthorization()
            locationManagers.requestWhenInUseAuthorization()
        }

        if #available(iOS 9.0, *) {
            //            locationManagers.allowsBackgroundLocationUpdates = true
        } else {
            // Fallback on earlier versions
        }
        locationManagers.desiredAccuracy = kCLLocationAccuracyBest
        locationManagers.pausesLocationUpdatesAutomatically = false
        locationManagers.distanceFilter = 0.1
        locationManagers.delegate = self

    }


    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {

    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else {
            return
        }
        self.lastLocation = location
        updateLocation(currentLocation: location)

    }

    @nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            locationManager?.requestWhenInUseAuthorization()
            break
        case .authorizedWhenInUse:
            locationManager?.startUpdatingLocation()
            break
        case .authorizedAlways:
            locationManager?.startUpdatingLocation()
            break
        case .restricted:
            // restricted by e.g. parental controls. User can't enable Location Services
            break
        case .denied:
            // user denied your app access to Location Services, but can grant access from Settings.app
            break
        default:
            break
        }
    }



    // Private function
    private func updateLocation(currentLocation: CLLocation){

        guard let delegate = self.delegate else {
            return
        }

        delegate.locationDidUpdateToLocation(currentLocation: currentLocation)
    }

    private func updateLocationDidFailWithError(error: NSError) {

        guard let delegate = self.delegate else {
            return
        }

        delegate.locationUpdateDidFailWithError(error: error)
    }

    func startUpdatingLocation() {
        print("Starting Location Updates")
        self.locationManager?.startUpdatingLocation()
        //        self.locationManager?.startMonitoringSignificantLocationChanges()
    }

    func stopUpdatingLocation() {
        print("Stop Location Updates")
        self.locationManager?.stopUpdatingLocation()
    }

    func startMonitoringSignificantLocationChanges() {
        self.locationManager?.startMonitoringSignificantLocationChanges()
    }

}

Upvotes: 0

Views: 1233

Answers (1)

vadian
vadian

Reputation: 285180

The location manager works asynchronously and provides delegate methods to get the result.

In your class adopt LocationServiceDelegate, implement the delegate methods and set the delegate for example in viewDidLoad

func locationDidUpdateToLocation(currentLocation: CLLocation)
{
    print(LocationSingleton.sharedInstance.lastLocation)
}

func locationUpdateDidFailWithError(error: NSError)
{
    print(error)
}

func viewDidLoad()
    super viewDidLoad()

    let locationSingleton = LocationSingleton.sharedInstance
    locationSingleton.delegate = self
    locationSingleton.startUpdatingLocation()
}

When a location is detected, one of the delegate methods is called

Upvotes: 3

Related Questions