Building Feng Shui Luopan, the part that looked simplest — “read the bearing from the sensor” — took the longest. The reason: iOS returns two kinds of heading, and they are not the same.
magneticHeading vs trueHeading
CLHeading exposes both. magneticHeading is measured against magnetic north; trueHeading against geographic north. The gap between them is magnetic declination, roughly 0–1° in Vietnam. That sounds tiny, but a luopan divides the circle into 24 mountains of 15° each — one degree at a boundary flips the reading entirely.
Traditional feng shui uses magnetic north: a real luopan is a magnetised needle. So the app must use magneticHeading, not trueHeading, however much more “accurate” the latter sounds.
trueHeading returns -1
Another trap: trueHeading returns a negative value when location permission has not been granted. Render that straight to the screen and users see a compass reading “-1°” and assume the app is broken.
Rule: always check
headingAccuracy >= 0before trusting any heading value.
Magnetic interference
The magnetometer is disturbed by anything metallic or magnetic: a MagSafe case, a steel desk, rebar inside concrete. iOS reports the disturbance through headingAccuracy — larger means less trustworthy. The app surfaces a warning past a threshold rather than silently reporting a wrong number.
Smoothing the reading
Raw values jitter constantly. But over-filter and the needle lags: you rotate the phone and the needle trails behind, which feels awful. The balance: a moving average over a short window, bypassed whenever the delta is large — that means the user is deliberately turning.
After a few iterations: the needle is steady when still, tracks closely when turning, and never drifts on its own — the minimum bar for a measuring tool.