CLLocation distance tracking starts off incorrectly

519 Views Asked by At

I am new to Swift (and this website, so sorry if I am doing anything wrong), and I am trying to make a running app that tracks the user's location. While the function I used to track the distance works, it doesn't start at 0. When I hit the start button, the distance starts at a random number and then it starts tracking from there.

My question is: Is there something I am not addressing something correctly? If so, is there a way to fix it so that the tracking is more accurate? Here is what I have so far:

override func viewDidLoad() {
    super.viewDidLoad()


    stopwatchLabel.text = "00:00.00"
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.delegate = self

    locationManager.requestAlwaysAuthorization()
    locationManager.activityType = .fitness
   locationManager.distanceFilter = 10.0
    mapView.showsUserLocation = true
    startLocation = nil

    // Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Location Delegate Methods


func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
    let location = locations.last
    let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
    let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002))
    self.mapView.setRegion(region, animated: true)


    if startLocation == nil {
        startLocation = locations.first
    }
    var distance = startLocation.distance(from: location!)
    let lastDistance = location?.distance(from: location!)
    distance += lastDistance!
    distanceString = "\(distance)"
    distanceLabel.text = distanceString


}

Here is what the app looks like:

the run screen

I realize that other people have asked similar questions, but the questions either have no answer, or they are in a different language (such as Objective-C). If this question has been answered before and I'm just overlooking it, could someone please link the answer to me? Thank you!

2

There are 2 best solutions below

7
rmp On BEST ANSWER

When the location manager starts, the first location returned is the cached, last know location. You need to check for this, via the timestamp, as well as check for the level of accuracy that is returned. Something like this in your didUpdateLocations delegate:

let newLocation = locations.last

    let timeDiff = newLocation?.timestamp.timeIntervalSinceNow

let accuracyNeeded:CLLocationAccuracy=100.0

    if timeDiff < 5.0 && (newLocation?.horizontalAccuracy)!<=accuracyNeeded{
        //your code here
    }
0
matt On

You have to allow the sensors time to warm up.

Here is a typical didUpdateLocations implementation. We keep track of both the time elapsed since we started updating locations and the improving horizontal accuracy as the sensors warm up. If the horizontal accuracy doesn't improve in a reasonable time, we give up.

You will need a nil property, a Date?, called startTime, and constants REQ_TIME and REQ_ACC. self.stopTrying() turns off updates and resets startTime to nil.

        let loc = locations.last!
        let acc = loc.horizontalAccuracy
        let time = loc.timestamp
        let coord = loc.coordinate
        if self.startTime == nil { // Date? property, keep track of time
            self.startTime = Date()
            return // ignore first attempt
        }
        let elapsed = time.timeIntervalSince(self.startTime)
        if elapsed > REQ_TIME { // required time before giving up
            self.stopTrying()
            return
        }
        if acc < 0 || acc > REQ_ACC { // desired accuracy
            return // wait for the next one
        }
        // got it
        print("You are at \(coord.latitude) \(coord.longitude)")