0

I am developing a running app in SwiftUI for watchOS. The problem is that when I start the run, the measuring works fine as 0.55 0.56 0.57 km, etc. Then it randomly freezes, and jumps from 0.57 straight to 0.71. Even the total distance is sometimes inaccurate. When I look at the map of the run, I can sometimes see sharp corners and straight lines in curves, implying very inaccurate measurement. The OS is watchOS 8.0. I have tried kCLLocationAccuracyBest to no avail.

I have used CLLocationManager in an ObservableObject called DistanceObservable, and I have this exact code in iOS app that measures the distance perfectly.

... @Published var distance: Double = 0.0
func startUpdate() {
        self.manager = CLLocationManager()
        self.manager?.delegate = self
        self.manager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        self.manager?.requestWhenInUseAuthorization()
        self.manager?.distanceFilter = 10.0
        self.manager?.allowsBackgroundLocationUpdates = true
        self.manager?.startUpdatingLocation()
}

Here is my watchOS didUpdateLocations method.

 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    var locations = locations

        for newLocation in locations {
            let howRecent = newLocation.timestamp.timeIntervalSinceNow
            guard newLocation.horizontalAccuracy < 20 && abs(howRecent) < 10 else { continue }
            
            if let lastLocation = locationList.last {
                let delta = newLocation.distance(from: lastLocation)
                
                if (delta < 60) { // To prevent inaccurate "jumps" in distance
                    distance = distance + delta
                    
                    if UserDefaults.standard.value(forKey: "hasSet") as!Bool == false {
                        distance = 0
                        locations.removeAll()
                        UserDefaults.standard.setValue(true, forKey: "hasSet")
                    }
                    
                    latPoints.append(Double(locationList.last!.coordinate.latitude))
                    lonPoints.append(Double(locationList.last!.coordinate.longitude))
                }
            }
            
            locationList.append(newLocation)
    }
 }

Finally I use the @EnvironmentObject in a SwiftUI View.

struct MyView: View {
    @EnvironmentObject var loc: DistanceObservable
    var body: some View {
        Text("\(loc.distance)")
    }
}
user7289922
  • 181
  • 1
  • 11
  • Are you sure the watch has not gone inactive or into the background? See [Working with the watchOS App Life Cycle](https://developer.apple.com/documentation/watchkit/wkextensiondelegate/working_with_the_watchos_app_life_cycle). The watch is a different animal than the phone to save on battery. What you are doing is VERY intensive for the watch. Also, added some APIs just for this sort of situation. The bottom line is apps don't translate 1:1 from iOS to watchOS. This is one of those places. – Yrb Oct 15 '21 at 21:13
  • I have solved the issue by adding HealtKit. You can see the answer below. Thanks! – user7289922 Oct 19 '21 at 09:32

1 Answers1

0

I have solved the issue by starting new HealthKit workout.

  1. import HealthKit
  2. Add conformance to ObservableObject and its methods
class SwiftUILocation: NSObject, CLLocationManagerDelegate, ObservableObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate  {

 func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
        print("A")
    }
    
    func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
        print("B")
    }
    
    func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
        print("C")
    }
    
    func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
        print("D")
    }
    
//.. rest of the code
}

  1. Start HealthKit at the start of the workout
 func startHealtKit(){
        let configuration = HKWorkoutConfiguration()
        configuration.activityType = .running
        configuration.locationType = .outdoor
           
        do {
            self.workoutSession = try HKWorkoutSession(healthStore: healthStore,
                                               configuration:configuration)
            
            self.builder = self.workoutSession?.associatedWorkoutBuilder()
            self.builder?.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
                                                               workoutConfiguration: configuration)
            
            self.workoutSession?.delegate = self
            self.builder?.delegate = self
            self.workoutSession?.startActivity(with: Date())
            self.builder?.beginCollection(withStart: Date()) { (success, error) in
                // Indicate that the session has started.
            }
        }
        catch {
            print("HealthKit problem \(error.localizedDescription)")
         
        }
    }
  1. Stop HealtkKit at the end of the workout

  func stopHealthKit(){
        self.builder?.workoutSession?.stopActivity(with: Date())
        
        self.builder?.endCollection(withEnd: Date()) { (success, error) in
            // Indicate that the session has started.
        }
    }
user7289922
  • 181
  • 1
  • 11