I have an app that I've recently converted to using Apple's new compass fusion API. It used to use older (now deprecated) APIs.
This is how I enable motion updates. If location services are not available I have to ask for magnetic north since the system cannot determine the magnetic declination to determine try north from magnetic north. If you don't do this the compass will just hang and nothing will happen
if ( [CLLocationManager headingAvailable] )
{
if ( [CLLocationManager locationServicesEnabled] )
[motionMgr startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical];
else
[motionMgr startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
}
I'm seeing there are other cases when true north cannot be gotten. If you put the device in airplane mode, it cannot lookup the declination on the internet and will fail. It is a little tricky to get it to fail this way because sometimes the system caches an old magnetic declination.
Does anyone know the preferred way to determine if the system can get true north? I could use the Reachability class, but that may be overkill. It may still be able to determine true north due to a cached value.
[Edit]
I did find another way to do this that seems somewhat robust. It looks like if you ask for true north headings and it can't determine true north, then the motion manager's deviceMotion will be nil when you ask for it. You need to do this after a second or two after starting it to allow the motion manager to get up and running.
[motionMgr startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical];
// Call checkForTrueNorth a second or so after starting motion updates to see if true north is available
// It needs a bit of time to get running. If it isn't available switch to using magnetic north.
[self performSelector:@selector(checkForTrueNorth) withObject:nil afterDelay:1.5];
- (void)checkForTrueNorth
{
if (motionMgr.deviceMotion == nil) // nil means it couldn't get true north.
{
[motionMgr stopDeviceMotionUpdates];
[motionMgr startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
}
}
My only concern about this method is I don't think it is documented behavior that nil is returned in this case. It works now but maybe not in future releases.
In addition to airplane mode (which can be tested for using Reachability), the other main reason true north is not available is when this system setting has been disabled:
Privacy / Location Services / System Services / Compass Calibration
Apple has not provided any way to enquire on this particular setting. However, you can test for true north being available by looking at the device's reported heading data, for example as follows: