What's new in Core Motion

Written by Ramit Sharma

Description: Learn how you can use the latest Core Motion updates to expand how your app uses motion data. Discover how to stream higher-frequency sensor data when recording a HealthKit workout on Apple Watch. We'll show you how you can get submersion data — including water depth and temperature — during water-based activities like snorkeling. Find out how to stream motion data like attitude, user acceleration, and rotation rate from audio devices like AirPods to connected devices like iPhone and Mac.

Core Motion Overview

  • CoreMotion serves as a central framework to access motion data from inertial sensors.
  • Crash detection, fall detection, and spatial audio are just some of the features that rely on improved sensing capabilities.
  • Capturing the way a device moves is central to how we experience them.
  • Many of Apple's devices use built-in sensors to help create a notion of their movement through space.
  • Apple Watch for example has built-in sensors include:
    • an accelerometer, which measures acceleration
    • a gyroscope, which measures rotation
    • a magnetometer, which measures the magnetic field
    • a barometer, which measures pressure.
    • Together, they help track how the device moves and orients in space.
    • Generating an idea of a device's movement is fundamental like steps taken, calories burned.
    • It supports experiences that rely on the orientation of the device, like with a stargazing app

Headphone motion

  • Dynamic head tracking relies on the same device motion algorithms that live on iPhone and Apple Watch.
  • CMHeadphoneMotionManager:
    • Introduced a couple years ago
    • Provided the same data that made dynamic head tracking possible
    • Head tracking unlocked features like gaming to fitness applications.
    • coming to macOS 14 and can be used to stream device motion from audio products that support spatial audio with dynamic head tracking.
    • Use CMDeviceMotion for data
    • CMDeviceMotion.SensorLocation for sensor location
    • CMHeadphoneMotionManagerDelegate for connection state

Headphone motion events

  • Adopt the CMHeadphoneMotionManagerDelegate protocol to respond to connection state updates.
  • If Automatic Ear Detection is enabled we receive events that impact head tracking.
  • If the buds are taken out of ear we get a disconnect event and a connect event when they're put back in.
func headphoneMotionManagerDidConnect(_ manager: CMHeadphoneMotionManger) {
	//	Tracking started with connect event

func headphoneMotionManagerDidDisConnect(_ manager: CMHeadphoneMotionManger) {
	//	Tracking stopped with connect event
  • if automatic head detection is enabled, putting on and taking off over ear headphones will trigger these events.
  • Setting up CMHeadphoneMotionManager:
    • make sure that device motion data is available by checking the isDeviceMotionAvailable property
    • Assign a delegate to receive the connection events
    • Then, start streaming data.
    • CMHeadphoneMotionManager exposes both a push and a pull interface to grab data.
    • Use startDeviceMotionUpdates and specify an operation queue and handler.
    • Users need to authorize your app for motion data using the Motion Usage Description key you add to your Info.plist.
    • can check the authorizationStatus property to confirm whether you've been authorized for motion data
    • Once authorized and data starts streaming, tracking head pose is easy using the attitude information provided with each device motion update.
// Start streaming headphone motion
let manager = CMHeadphoneMotionManager()
guard manger.isDeviceMotionAvailable else {
manager.delegate = self

manager.startDeviceMotionUpdates(to: queue) { (motion, error) in
	guard let motion else {
//Track head movement using device motion
let currentPose = motion.attitude
  if let startingPose {
	  currentPose.Multiply(byInverseOf: startingPoint)
  • Along with the attitude, user acceleration, and rotation rate data, each device motion update contains sensor location information.
  • motion data is delivered to you from one bud at a time.

Sensor location

  • Use SensorLocation to Identify which bug produced the data
  • The bud streaming the data can be impacted by a number of things, including in-ear state if Automatic Ear Detection is enabled.
	public enum SensorLocation: Int {
	casedefault’ = 0
	case headphoneLeft = 1
	case headphoneRight = 2



  • Available on Apple Watch Ultra running watchOS 9.0
  • Uses the built-in barometer to tracks metrics during water-based activities.
  • Further CMWaterSubmersionManagerDelegate is used for:
    • Submersion and depth state
    • Surface air pressure
    • Water Depth
    • Temperature
    • Add the Shallow Depth and Pressure capability
    • Configure Auto Launch settings
// start tracking water submersion state
guard CMWaterSubmersionManager.waterSubmersionAvailable else {

let waterSubmersionManager = CMWaterSubmersionManager()

//Assign a delegate to recieve events and measurement updates
waterSubmersionManager.delegate = self 

Getting submersion updates with CMWaterSubmersionManagerDelegate

  • Submersion event
func(_ manager: CMWaterSubmersionManager, didUpdate event: CMWaterSubmersionEvent) {

	var submerged: Bool ?
	switch event.state {
	case .unknown:
		submerged = nil
	case .notSubmerged:
		submerged = false
	case .submerged:
		submerged = true
	@unknown default:
		fatalError(“Unknown submersion event: \(event.state)”)
	// Handle water submersion update
  • Error
func manager(_ manager: CMWaterSubmersionManager, errorOccurred error: Error) {
	// Handle error received when there are problems delivering submersion updates
  • Temperature update
func manager(_ manager: CMWaterSubmersionManager, didUpdate measurement: CMWaterTemperature) {

	let temp = measurement.temperature
	let uncertainty = measurement.temperatureUncertainty
	let currentTemperature = “\(temp.value) * \(uncertainty.value) \(temp.unit)” 
	// Handle current water temperature update
  • Measurement update
func manager(_ manager: CMWaterSubmersionManager, didUpdate measurement: CMWaterSubmersion) {
	var current Depth: String
	if let depth = measurement.dept {
		currentDepth = “\(depth.value) \(depth.unit)”
	} else {
		currentDepth = “None”
	// Handle new measurement update
	// Similar to depth, pressure and surface contain both a value and unit

Depth State

  • Out of water, its the notSubmerged state.
  • Above 1 meter under water its submergedShallow state.
  • Beyond 1 meter, its the submergedDeep state.
  • Shallow Depth and Pressure, ensures the app to stay within depth zones that minimize the risk of decompression sickness.
  • It keeps the maximum depth at 6 meters, and prompts when you’re close to that.
  • 6 meters, you'll enter the approachingMaxDepth state.
  • Beyond 6 meters, you're in the pastMaxDepth state.
  • Data is vended down to 6 meters, plus some uncertainty in the pastMaxDepth state.
  • Beyond that, you're in the sensorDepthError state.

Batched sensors

Comparing motion interface:

How sensor data is delivered

  • Device motion algorithms fuse data from the built-in accelerometer and gyroscope to provide an easy way to track the way a device, moving through space.
  • CMMotionManager delivers samples on a per-sample basis to your app in real time.
  • The maximum supported frequency is 100 Hz hence it's a great choice for low latency requirements, like UI components that rely on the instantaneous attitude of the device.

how high rate data is delivered using the new CMBatchedSensorManager

  • CMBatchedSensorManager provides batches of sensor data on a fixed schedule, delivering a batch of data per second.
  • Higher rate data is delivered at a lower overhead to your app.
  • That's 800 Hz accelerometer and 200 Hz device motion, compared to 100 Hz with the existing CMMotionManager.
  • Access some data streams that power the features that keep us safe, like fall and crash detection.
  • If your app has workout-centric features that can benefit from high rate data, but without very tight latency requirements, then CMBatchedSensorManager is well suited.

Using high- rate motion data

  • Evaluating a swing:
    • A swing has a couple different phases.
    • Pre-swing setup, the actual swing and then the post-impact follow-through.
    • time to contact is also an important metric for swing quality.
    • Δt = time to contact, from (start of swing to Impact)
    • First detect the point of impact between the bat and the ball using 800 Hz accelerometer.
    • Then detect the start of the swing using rotation along gravity with 200 Hz device motion.
    • Compute time to contact.
    • Then use CMBatchedSensorManager to start streaming and processing data.
    • this a workout-centric API, you need to have an active HealthKit workout session to get data.
    • Swift async support, it's easy to receive batches of sensor data and process each batch.
    • Make sure you evaluate for conditions to exit the loop.

Pick the right interface for you

  • CMMotionManager or CMBatchedSensorManager
  • 100 Hz maximum or 200 Hz device motion and 800 Hz accelerometer
  • Per-sample dispatch or batched delivery schedule
  • Low-latency requirements/Motion-based features outside of workouts or workout-centric features that can benefit from high-rate data. It's available on Apple Watch Series 8 and Ultra.

Missing anything? Corrections? Contributions are welcome 😃


Written by

Ramit Sharma

Ramit Sharma

Indie iOS Dev. Swift, SwiftUI, Obj-C, UX and related.