0

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!

Brandon
  • 23
  • 1
  • 5

2 Answers2

0

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
    }
rmp
  • 3,503
  • 1
  • 17
  • 26
  • I tried this method but on line 3, I get an error saying "Binary operator '<' cannot be applied to operands of type'TimeInterval?' and 'Double.' – Brandon Mar 19 '17 at 04:50
  • Show your exact code so I can see what you are doing. The code above runs fine as is. – rmp Mar 20 '17 at 15:34
  • ` let timeDiff = location?.timestamp.timeIntervalSinceNow if timeDiff < 5.0 && (location?.horizontalAccuracy)!<=self.accuracyNeeded{ if startLocation == nil { startLocation = locations.first } var distance = startLocation.distance(from: location!) let lastDistance = location?.distance(from: location!) distance += lastDistance! distanceString = "\(distance)" ` – Brandon Mar 20 '17 at 16:56
  • I just copied and pasted your code in xcode and get no error. I use the same code in several different apps without errors. There may be a different issues. How did you define accuracyNeeded? I added it to my example. – rmp Mar 20 '17 at 17:46
  • I'm not sure if this is right, but in my view did load function, I defined the accuracy as: locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation – Brandon Mar 21 '17 at 17:17
  • That will sent how accurate the manager tries to get but you will still want to check the accuracy of the location object returned as in most cases the first location returned may not be very accurate and you may want to disregard it. This is what the check above is doing. You just need to set how accurate you want the location to be before you record it. – rmp Mar 21 '17 at 17:24
  • I figured out what the issue was. I needed an exclamation mark instead of a question mark for: let timeDiff = newLocation?.timestamp.timeIntervalSinceNow – Brandon Mar 21 '17 at 17:36
0

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)")
matt
  • 515,959
  • 87
  • 875
  • 1,141