Can
Can

Reputation: 4726

Can't read variable of singleton object

I've implemented a singleton object for CoreLocation's location manager for an app I'm developing using the class constant method for Swift 1.2 and above explained here.

Although when I try to access the currentLocation variable either directly or using the getter method, I get nil.

What am I missing?

Implementation

import Foundation
import CoreLocation

class LocationService: NSObject, CLLocationManagerDelegate {
  static let sharedInstance = LocationService()

  var locationManager: CLLocationManager!
  var currentLocation: CLLocationCoordinate2D!
  var currentDirection: StepDirection!

  private override init() {
    super.init()

    locationManager = CLLocationManager()

    locationManager.requestAlwaysAuthorization()

    locationManager.delegate = self

    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    locationManager.headingFilter = kCLHeadingFilterNone
    locationManager.distanceFilter = 1
  }

  // MARK: Control Methods
  func startUpdatingLocation() {
    locationManager.startUpdatingLocation()
    print("Location updates are started.")
  }

  func stopUpdatingLocation() {
    locationManager.stopUpdatingLocation()
    print("Location updates are stopped.")
  }

  func startUpdatingHeading() {
    locationManager.startUpdatingHeading()
    print("Compass updates are started.")
  }

  func stopUpdatingHeading() {
    locationManager.stopUpdatingHeading()
    print("Compass updates are stopped.")
  }

  // MARK: CoreLocation Location Updates
  func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    // If location data can be determined
    if let location = locations.last! as CLLocation! {
      currentLocation = location.coordinate
//      print("Current Location: \(currentLocation)")
      NSNotificationCenter.defaultCenter().postNotificationName("LocationUpdate", object: self,
                                                                userInfo: ["longitude": currentLocation.longitude,
                                                                  "latitude": currentLocation.latitude])
    }
  }

  func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
    print("Location Manager: \(error)")

    NSNotificationCenter.defaultCenter().postNotificationName("LocationUpdateError", object: self,
                                                              userInfo: nil)
  }

  // MARK: CoreLocation Heading Updates
  func locationManager(manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
    let trueHeading = newHeading.trueHeading

    var declanation = (newHeading.trueHeading - newHeading.magneticHeading)
    declanation = 50.0

    if (0.0+declanation <= trueHeading) && (trueHeading <= 90.0+declanation) {
      currentDirection = StepDirection.Right
    } else if (90.0+declanation < trueHeading) && (trueHeading <= 180.0+declanation) {
      currentDirection = StepDirection.Down
    } else if (180.0+declanation < trueHeading) && (trueHeading <= 270.0+declanation) {
      currentDirection = StepDirection.Left
    } else if (270.0+declanation < trueHeading) && (trueHeading <= 360.0+declanation) {
      currentDirection = StepDirection.Up
    }

    NSNotificationCenter.defaultCenter().postNotificationName("CompassUpdate", object: self, userInfo: ["currentDirection": currentDirection.rawValue])
  }

  func locationManagerShouldDisplayHeadingCalibration(manager: CLLocationManager) -> Bool {
    return true
  }

  // MARK: Access Methods
  func getCurrentLocation() -> CLLocationCoordinate2D! {
    return currentLocation
  }
}

Access

I first tried accessing it like the following:

LocationService.sharedInstance.currentLocation or LocationService.sharedInstance.getCurrentLocation

I then assigned the shared instance to a variable thinking that I wasn't preserving the state:

locationService = LocationService.sharedInstance

And then using the access methods or the variable names:

locationService.currentLocation or locationService.getCurrentLocation

Upvotes: 0

Views: 124

Answers (1)

Fred Faust
Fred Faust

Reputation: 6800

You need to call your startUpdating function to get the location manager to start updating the location.

private override init() {
super.init()

locationManager = CLLocationManager()

locationManager.requestAlwaysAuthorization()

locationManager.delegate = self

locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.headingFilter = kCLHeadingFilterNone
locationManager.distanceFilter = 1
locationManager.startUpdatingLocation()
}

As you asked in comments, the location manager will continuously attempt to get a user's location until stop updating is called.

Discussion This method returns immediately. Calling this method causes the location manager to obtain an initial location fix (which may take several seconds) and notify your delegate by calling its locationManager:didUpdateLocations: method. After that, the receiver generates update events primarily when the value in the distanceFilter property is exceeded. Updates may be delivered in other situations though. For example, the receiver may send another notification if the hardware gathers a more accurate location reading.

You can read more here: https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/startUpdatingLocation

You can access new location data from the delegate method using the location property of the location manager (which you already do in your delegate method).

Upvotes: 1

Related Questions