Thank you — this is exactly the kind of answer I was hoping for, and the point about background categories following user-facing functionality rather than implementation convenience is taken.
Let me answer your question directly.
Why near real time
The app's entire user-facing output is a single local notification whose meaning is "this is happening right now." It is an interruption, not a record. Once the device has been held past the threshold angle continuously for a short hold period, the notification has to arrive while that is still true. A notification that arrives afterwards describes a moment the user can no longer act on, which removes the reason for the feature to exist. Concretely, my latency budget is seconds to tens of seconds, not minutes.
With that said, I want to separate two parts of your suggestion, because I think you are right about one and I am not sure about the other.
Sampling. I take your point completely. The failure mode you describe — events silently delayed or dropped while the app treats the stream as evenly spaced — is a real weakness in what I have now, and I had not been checking timestamp the way I should. Polling CMSensorRecorder.accelerometerData(from:to:) on a fixed interval is clearly the more robust way to obtain the data, and I would like to move to it regardless of how the rest of this resolves.
Evaluation and notification. This is where I am stuck. Polling still requires my process to be executing. If the app is suspended, nothing polls, nothing evaluates the samples against the threshold, and no notification is scheduled. The recorder faithfully accumulates data that no one reads until the user next opens the app — at which point the notification would be describing something that ended long ago. So as far as I can tell, CMSensorRecorder fixes the quality and completeness of the data, but not the requirement that my code be running at a tens-of-seconds cadence.
So what I would most like to ask:
Am I wrong about the above? Is there a wake-up path associated with CMSensorRecorder, or anywhere else, that gets my code running periodically without a background mode?
BGAppRefreshTask is the periodic-background-work mechanism I am aware of, but my understanding is that it is opportunistic and typically scheduled on the order of tens of minutes or longer, which is well outside the budget above. Is that understanding accurate?
Following your point that background categories are determined by the functionality provided to the user, it may help if I describe that functionality concretely. The app is a posture-awareness tool. It watches the angle at which the user is holding their iPhone, and when the device has been held tilted forward past a threshold for a sustained period — the head-down position people settle into while reading on a phone — it raises a local notification so the user can correct it. That is the whole of it. And to be clear on your point, it never reads, stores, or transmits the user's location.
Given that, is there a supported background execution mechanism I should be using instead? Or is it simply that iOS does not provide continuous background execution for this class of app, and I should be designing around a foreground-only or opportunistic model?
I would rather build what the platform actually supports than lean on something I should not be leaning on. A plain answer to (3), including "no," is the most useful thing you could give me.
Thank you again for the detailed reply — the CMSensorRecorder point in particular is something I would not have arrived at on my own.