How to access the barometer in iOS 8

image

The iPhone 6 introduced a new piece of hardware: a barometer with which we can detect air pressure and the device’s relative altitude. With it, iOS 8 gave us a couple of classes to access this data as part of the Core Motion framework.

Let’s see how we can access those barometer values.

CMAltimeter: starting to measure altitude

The first thing we’ll need to do is tell our class that we want to use the Core Motion framework. Let’s import it:

@import CoreMotion;

We also want to hold a property to the CMAltimter so that we can start and stop it. While we’re here, let’s initialise it with a custom initialiser method:

@property (strong, nonatomic) CMAltimeter *altimeter;

// ...

- (CMAltimeter *)altimeter {
    
    if (!_altimeter) {
        _altimeter = [[CMAltimeter alloc]init];
    }
    return _altimeter;
}

We can start our altimeter with a method called startRelativeAltitudeUpdates, and stop it again with stopRelativeALtitudeUpdates. Before we do however, it’s wise to check if the current hardware actually has a barometer.

Thankfully the CMAltimeter class has a property called isRelativeAltitudeAvailable: if it returns YES we have barometer – if not, we can’t see how high we’re flying.

Here’s a method that would check and start our barometer updates:

- (IBAction)trackAltitude {
    
    // check if the barometer is available
    if (![CMAltimeter isRelativeAltitudeAvailable]) {
        
        NSLog(@"Barometer is not available on this device. Sorry!");
        return;
    
    } else {
        
        NSLog(@"Barometer is available.");
    }
    
    // start altitude tracking
    [self.altimeter startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAltitudeData * _Nullable altitudeData, NSError * _Nullable error) {
        
        // this block is called every time there's an update
        // access data here (for example, populate labels)
       [self updateLabels:altitudeData];
    }];
}

You can run this method on a background thread if you like, in which case replace the mainQueue with your own. The handler block is called every time the altimeter sends new data (roughly every second).

Updates are only delivered as long as the app is actively in the foreground. When the app comes back from the background, updates resume – but nothing will be delivered when another app is active.

Accessing barometer data

The startRelativeAltitudeUpdatesToQueue method contains a handler block as part of the method signature. This means that Xcode code completion can help us access those values. The data is passed as a CMAltitudeData object. For testing, let’s populate a set of labels.

Here’s how we might do that:

- (void)updateLabels:(CMAltitudeData *)altitudeData {
    
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
    formatter.maximumFractionDigits = 2;
    formatter.minimumIntegerDigits = 1;
    
    NSNumber *timestamp = [NSNumber numberWithDouble:altitudeData.timestamp];
    NSString *timeInterval = [NSString stringWithFormat:@"%@", [formatter stringFromNumber:timestamp]];
    NSString *altitude = [NSString stringWithFormat:@"%@", [formatter stringFromNumber:altitudeData.relativeAltitude]];
    NSString *pressure = [NSString stringWithFormat:@"%@", [formatter stringFromNumber:altitudeData.pressure]];
    
    self.label1.text = [NSString stringWithFormat:@"Time Interval: \n%@", timeInterval];
    self.label2.text = [NSString stringWithFormat:@"Relative Altitude: \n%@", altitude];
    self.label3.text = [NSString stringWithFormat:@"Air Pressure: \n%@", pressure];
}

Since most values are returned as floating point numbers, I’ve used a number formatter to curb the digits after the decimal point to two digits.

The CMAltitudeData is a child of CMLogItem, so we have access to a total of three properties:

  • timestamp (an NSTimeInterval in seconds since the device has been booted)
  • relativeAltitude (an NSNumber in meters)
  • pressure (an NSNumber in kilopascal)

The timestamp and pressure properties are relatively straightforward, but the altitude value needs a little explanation: the device doesn’t really know how high up it is right now, but it can track how far up or down you are travelling with it.

So the altitude value will deliver something around zero when we receive our first events, and will show positive values when we travel to higher ground, and negative values when we travel further down. One floor is approximately 3 meters.

Demo Project

Check out my simple demo project on GitHub:

Further Reading





Leave a Reply