CMAltimeter not delivering relative altitude updates

I want to get the barometric pressure reading from the built-in barometer to display it in my iOS app. When I use CMAltimeter.startRelativeAltitudeUpdates(), my app receives no relative altitude update events, which means I can't view the barometric pressure from the sensor (because that's only contained in CMAltitudeData, not CMAbsoluteAltitudeData. If I use CMAltimeter.startAbsoluteAltitudeUpdates(), I get absolute altitude update events every second or so. NSMotionUsageDescription is set.

I have tried the following things, all of which haven't worked:

  • Only calling startRelativeAltitudeUpdates() and not startAbsoluteAltitudeUpdates()
  • Calling CMSensorRecorder.recordAccelerometer(forDuration: 0.1), as suggested in this thread
  • Calling CMMotionActivityManager.queryActivityStarting(from: .now, to: .now, to: .main), as suggested here
  • Physically moving my iPhone up and down about 200 feet using an elevator; I see absolute altitude updates which are in line with what's expected, but still receive no relative altitude update events
  • Calling the same APIs in a watchOS app on an Apple Watch Series 10; I see much less frequent absolute altitude updates, and still no relative altitude updates

I know the barometer sensor is working, because when I move my iPhone up and down even a foot or two indoors, I see an immediate change in the absolute altitude reading that I know wouldn't come from GPS.

This example code, when run on my iPhone 16 Pro running iOS 18.1.1, prints updates started and then updating absolute every second, but doesn't print anything else. The absolute altitude, accuracy, and authentication status fields update (and the auth status shows 3, indicating .authorized), but the relative altitude and pressure fields remain as --.

struct ContentView: View {
    @State private var relAlt: String = "--"
    @State private var relPressure: String = "--"
    @State private var absAlt: String = "--"
    @State private var precision: String = "--"
    @State private var accuracy: String = "--"
    @State private var status: String = "--"

    
    var body: some View {
        VStack {
            Text("Altitude: \(relAlt) m")
                .font(.title3)
            Text("Pressure: \(relPressure) kPa")
                .font(.title3)
            
            Text("Altitude (absolute): \(absAlt) m")
                .font(.title3)
            Text("Precision: \(precision) m")
                .font(.title3)
            Text("Accuracy: \(accuracy) m")
                .font(.title3)
            Text("Auth status: \(status)")
                .font(.title3)
        }
        .padding()
        .onAppear {
            let altimeter = CMAltimeter()

            startRelativeBarometerUpdates(with: altimeter)
            startAbsoluteBarometerUpdates(with: altimeter)
            status = CMAltimeter.authorizationStatus().rawValue.formatted()
            print("updates started")
        }
    }
    
    private func startRelativeBarometerUpdates(with altimeter: CMAltimeter) {
        guard CMAltimeter.isRelativeAltitudeAvailable() else {
            relAlt = "nope"
            relPressure = "nope"
            return
        }
        
        altimeter.startRelativeAltitudeUpdates(to: .main) { data, error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }
            
            if let data = data {
                print("updating relative")
                relAlt = String(format: "%.2f", data.relativeAltitude.doubleValue)
                relPressure = String(format: "%.2f", data.pressure.doubleValue)

            } else {
                print("no data relative")
            }
        }
    }
    
    private func startAbsoluteBarometerUpdates(with altimeter: CMAltimeter) {
        guard CMAltimeter.isAbsoluteAltitudeAvailable() else {
            absAlt = "nope"
            print("no absolute available")
            return
        }
        
        let altimeter = CMAltimeter()
        altimeter.startAbsoluteAltitudeUpdates(to: .main) { data, error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }
            
            if let data = data {
                print("updating absolute")
                absAlt = String(format: "%.2f", data.altitude)
                precision = String(format: "%.2f", data.precision)
                accuracy = String(format: "%.2f", data.accuracy)

            }
        }
    }
}

Is this behavior expected? How can I trigger delivery of relative altitude updates to my app?

Answered by robinsonz in 817118022

I managed to solve this myself. It turns out initializing the CMAltimeter in the onAppear function caused the reference to not stick around, which for unclear reasons meant that relative altitude updates would never be delivered even though absolute updates were. Moving

let altimeter = CMAltimeter()

to the top level of the file made things work perfectly.

Accepted Answer

I managed to solve this myself. It turns out initializing the CMAltimeter in the onAppear function caused the reference to not stick around, which for unclear reasons meant that relative altitude updates would never be delivered even though absolute updates were. Moving

let altimeter = CMAltimeter()

to the top level of the file made things work perfectly.

CMAltimeter not delivering relative altitude updates
 
 
Q