Search code examples
iosauthorizationcore-motioncmaltimeter

iPhone: No barometric pressure data in iOS 17.4


My app uses the barometric data from the CMAltimeter on iOS (iPhone). That has always worked just fine. But after installing iOS 17.4, it stopped working. Other apps that use barometric data also stopped working. So it appears to be a major fault in iOS 17.4.

Below is the (Swift) code used to test this. This code used to work fine with earlier versions of iOS, but in 17.4, the authorizationStatus() returns .restricted, and the handler is called with the error:

The operation couldn’t be completed. (CMErrorDomain error 105.)

This all indicates there is some authentication failure. I have logged bug report with Apple.

But I wonder if others experienced the same, and if there is some work-around/solution that makes barometric data available again.

if CMAltimeter.isRelativeAltitudeAvailable() {
    status = CMAltimeter.authorizationStatus()
    self.altimeter.startRelativeAltitudeUpdates(to: OperationQueue.main) { (data, _error) in
        DispatchQueue.main.async {
            error = _error != nil ? _error!.localizedDescription : "<nil>"
            print(error)
            pressure = data != nil ? String(format: "%f", data!.pressure.doubleValue) : ""
        }
    }
}

PS: the documentation of Core Motion (which CMAltimeter is part of) says that the NSMotionUsageDescription .info.plist property needs to be defined for all Core Motion service. I have that property in place, but it does not seem to make a difference.


Solution

  • Note that the following is a work-around. I urge everybody to file a bug-report with Apple, to make sure there is pressure on Apple to fix this.

    First of all, the NSMotionUsageDescription key must be defined in the app's .info.plist file.

    Others have suggested the CMSensorRecorder work-around. It turns out that the "Fitness Tracking" setting in the Settings App needs to be enabled for that to work.

    When the authorizationStatus is .restricted, then the user first needs to switch on Settings > "Privacy & Security" > "Motion & Fitness" > "Fitness Tracking"

    When the authorizationStatus is .notDetermined, then start the CMSensorRecorder. A "Motion & Fitness" privacy settings will be added to your app's settings, and the user will be asked to authorise by iOS. When the user accepts, authorizationStatus changes to .authorized and you can start the CMAltimeter.

    When the authorizationStatus is .denied, the user needs to switch on the "Motion & Fitness" privacy setting of your app.

    The following code worked for me:

    let status = CMAltimeter.authorizationStatus()
    switch status {
    case .notDetermined:
        // trigger a authorization popup
        let recorder = CMSensorRecorder()
        DispatchQueue.global().async {
           recorder.recordAccelerometer(forDuration: 0.1)
        }
    case .restricted:
        popupText = "Please switch \"Fitness Tracking\" on, in the Apple Settings app, under \"Privacy & Security\" > \"Motion & Fitness\""
        showingPopup = true
    case .denied:
        popupText = "Please switch \"Motion & Fitness\" on, in the app settings"
        showingPopup = true
    default:
        print("authorized")
    }
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
       let status = CMAltimeter.authorizationStatus()
       if status == .authorized {
          timer.invalidate()
          self.altimeter.startRelativeAltitudeUpdates(to: OperationQueue.main) { (data, _error) in
             // handle altimeter data
          }
       }
    }