WWDC Quick Look 💓 By SwiftGGTeam
What's new in Core Motion

What's new in Core Motion

Watch original video

Highlight

Core Motion adds three new capabilities in iOS 17 and watchOS 10: AirPods head tracking data support for macOS, Apple Watch Ultra underwater depth and temperature monitoring, and HealthKit high-frequency sensor data streaming during exercise (800Hz accelerometer / 200Hz device motion).

Core Content

Sports data is the invisible infrastructure of the Apple ecosystem. Step counting, collision detection, spatial audio - these experiences are all based on sensors such as accelerometers, gyroscopes, and barometers. Core Motion serves as a unified portal for developers to use this data.

AirPods head tracking expanded to macOS

(02:22) Dynamic head tracking for spatial audio relies on the same motion algorithms as iPhone and Apple Watch.CMHeadphoneMotionManagerStarting from iOS 14, this data will be opened up, allowing apps to track the posture, acceleration, and rotation of the user’s head.

This year this capability is expanded to macOS 14.

import CoreMotion

let headphoneManager = CMHeadphoneMotionManager()
headphoneManager.delegate = self

// Check whether the device is supported
if headphoneManager.isDeviceMotionAvailable {
    headphoneManager.startDeviceMotionUpdates(to: .main) { motion, error in
        guard let motion = motion else { return }

        // Head attitude (pitch, yaw, roll)
        let attitude = motion.attitude

        // User acceleration
        let acceleration = motion.userAcceleration

        // Rotation rate
        let rotation = motion.rotationRate

        // Data source: left ear or right ear
        let location = motion.sensorLocation // .left or .right
    }
}

Key points:

  • Supports headphones such as AirPods Pro that support spatial audio dynamic head tracking -sensorLocationDistinguish whether the data comes from the left ear or the right ear
  • After turning on automatic in-ear detection, taking out one ear will automatically switch to the other ear to continue transmission.
  • Requires configuration in Info.plistMotion Usage DescriptionPermission description

Connection status monitoring:

extension ViewController: CMHeadphoneMotionManagerDelegate {
    func headphoneMotionManagerDidConnect(_ manager: CMHeadphoneMotionManager) {
        // Headphones are connected; data can start being received
    }

    func headphoneMotionManagerDidDisconnect(_ manager: CMHeadphoneMotionManager) {
        // Headphones disconnected; stop related features
    }
}

Key points:

  • The delegate method is triggered when the connection status changes
  • When automatic in-ear detection is turned on, taking out/putting in the headphones will also trigger disconnect/connect
  • Wearing detection of headphones is also supported

Apple Watch Ultra underwater data monitoring

07:22CMWaterSubmersionManagerUse Apple Watch Ultra’s barometer to track water depth and temperature during water activities like snorkeling, swimming, and more.

import CoreMotion

let submersionManager = CMWaterSubmersionManager()
submersionManager.delegate = self

Delegate implementation:

extension WorkoutManager: CMWaterSubmersionManagerDelegate {
    // Submersion state changes (entering/leaving water)
    func waterSubmersionManager(
        _ manager: CMWaterSubmersionManager,
        didUpdate event: CMWaterSubmersionEvent
    ) {
        switch event.state {
        case .notSubmerged:
            // At the water surface
        case .submergedShallow:
            // Within 1 meter underwater
        case .submergedDeep:
            // More than 1 meter underwater
        case .approachingMaxDepth:
            // Approaching the maximum depth of 6 meters
        case .pastMaxDepth:
            // Deeper than 6 meters
        case .sensorDepthError:
            // Sensor is out of range
        }
    }

    // Depth, pressure, and water temperature measurements
    func waterSubmersionManager(
        _ manager: CMWaterSubmersionManager,
        didUpdate measurement: CMWaterSubmersionMeasurement
    ) {
        let depth = measurement.depth?.value      // Water depth (meters)
        let pressure = measurement.pressure?.value // Water pressure
        let surfacePressure = measurement.surfacePressure?.value // Surface air pressure
    }

    // Water temperature updates
    func waterSubmersionManager(
        _ manager: CMWaterSubmersionManager,
        didUpdate temperature: CMWaterTemperature
    ) {
        let waterTemp = temperature.temperature.value // Water temperature
        let uncertainty = temperature.uncertainty     // Uncertainty
    }
}

Key points:

  • Supported by Apple Watch Ultra only, requires watchOS 9+
  • Need to add “Shallow Depth and Pressure” capability
  • Water temperature data is only available in the immersed state. The uncertainty is high when first entering the water and converges over time.
  • The maximum monitoring depth is 6 meters, entering after exceedingpastMaxDepthStatus
  • Depth partitioning:notSubmergedsubmergedShallow(less than 1m) →submergedDeep(greater than 1m)

High frequency sensor data stream CMBatchedSensorManager

(11:35) Apple Watch Series 8 and Ultra support new high-frequency sensor data streams. with existingCMMotionManager(up to 100Hz, real-time push sample by sample) different,CMBatchedSensorManagerData is transferred in batches every second, up to 800Hz for accelerometers and 200Hz for device motion.

Comparison of two data acquisition methods:

FeaturesCMMotionManagerCMBatchedSensorManager
Accelerometer frequencyUp to 100Hz800Hz
Equipment movement frequencyMaximum 100Hz200Hz
Transmission methodSample-by-sample real-time pushBatch transmission per second
LatencyLow (sub-second level)High (second level)
Usage scenariosUI feedback, real-time controlMotion analysis, post-event calculations
PrerequisitesNoneRequires active HealthKit Workout

(13:18) The speaker used the baseball swing as an example to demonstrate the value of high-frequency data. During the entire swing, which lasts about 0.3 seconds, 100Hz sampling can only obtain 30 data points, while 800Hz can obtain 240 - enough to distinguish the subtle vibration differences at the moment of impact.

Detailed Content

Usage process of high-frequency data flow

(16:17) UseCMBatchedSensorManagerRequires an active HealthKit Workout Session:

import CoreMotion
import HealthKit

let batchedManager = CMBatchedSensorManager()

// 1. Confirm device support
guard batchedManager.isAccelerometerSupported else { return }

// 2. Start a HealthKit Workout
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .baseball
// ... configure and start the workout session

// 3. Use Swift async to receive batched data
Task {
    do {
        for try await batch in batchedManager.accelerometerUpdates() {
            processAccelerometerBatch(batch)
        }
    } catch {
        // Handle authorization errors or unsupported platforms
    }
}

Key points:

  • isAccelerometerSupportedandisDeviceMotionSupportedCheck device support
  • Only supported by Apple Watch Series 8 and Ultra
  • Must be in an active HealthKit Workout to get data
  • Process data batch by batch using Swift async/await interface
  • You need to check whether the workout is over during the loop to avoid infinite loops

Algorithm implementation of swing analysis

(17:08) The speaker showed how to calculate “time to contact” using high-frequency data:

Step 1: Detect the moment of impact

func detectImpact(from batch: [CMAccelerometerData]) -> TimeInterval? {
    // The z-axis is perpendicular to the crown direction and shows clear vibration at impact
    let filteredZ = batch.map { highPassFilter($0.acceleration.z) }

    // Find the peak of the filtered signal
    guard let maxIndex = filteredZ.enumerated().max(by: { $0.element < $1.element })?.offset else {
        return nil
    }

    // Get the corresponding timestamp from the raw data
    return batch[maxIndex].timestamp
}

Key points:

  • The z-axis direction is perpendicular to the Apple Watch crown
  • Use a high-pass filter to isolate high-frequency impact signals
  • Peak corresponds to the moment of impact

Step 2: Detect Swing Initiation

func detectSwingStart(impactTime: TimeInterval, motionBuffer: [CMDeviceMotion]) -> TimeInterval? {
    // Traverse backward from impactTime and look for the rotation-rate component along gravity
    // Find the transition point where it falls from above the threshold to below it

    for sample in motionBuffer.reversed() {
        // Only check samples before impactTime
        guard sample.timestamp < impactTime else { continue }

        // Swing duration should not exceed a reasonable range, such as 500 ms
        guard impactTime - sample.timestamp < 0.5 else { break }

        // Compute the rotation-rate component along gravity
        let rotationAlongGravity =
            sample.rotationRate.x * sample.gravity.x +
            sample.rotationRate.y * sample.gravity.y +
            sample.rotationRate.z * sample.gravity.z

        // Look for the transition from above the threshold to below it
        if abs(rotationAlongGravity) < swingThreshold {
            return sample.timestamp
        }
    }

    return nil
}

Key points:

  • Traverse device motion data forward from impactTime
  • The component of the rotation rate along the direction of gravity reflects the rotation of the wrist around the body
  • This component is significantly non-zero during the swing and close to zero at rest
  • Set a reasonable swing duration upper limit to filter noise

Step 3: Calculate contact time

func computeTimeToContact(impactTime: TimeInterval, swingStartTime: TimeInterval?) -> TimeInterval? {
    guard let startTime = swingStartTime else { return nil }

    // Verify that accumulated rotation during the swing is within a reasonable range
    let accumulatedRotation = computeAccumulatedRotation(from: startTime, to: impactTime)
    guard accumulatedRotation > minRotationThreshold && accumulatedRotation < maxRotationThreshold else {
        return nil
    }

    return impactTime - startTime
}

Key points:

  • Contact time = timestamp of moment of impact - timestamp of start of swing
  • Verify whether the detected swing is valid by accumulating rotation angles
  • Filter false detections (such as waving, adjusting posture and other non-swinging movements)

Signal difference between hits and misses

(20:45) The speaker compared the accelerometer signals of a hit ball and a missed ball. The swing itself is similar in both cases, but the vibration pattern at impact is noticeably different—a sharp impact peak on impact and a more gentle vibration on miss. The 800Hz data stream captures this difference, 100Hz makes it indistinguishable.

Core Takeaways

1. Head tracking driven spatial audio game

  • What: Use AirPods head gesture data to control game perspective or menu selections
  • Why it’s worth doing: Users don’t need to hold the device and can operate it by turning their head, suitable for fitness and VR experiences.
  • How to start:CMHeadphoneMotionManagergetattitudeData, mapped to the Euler angles of the game camera

2. Water Sports Recording App

  • What to do: Record depth curves, water temperature changes, and underwater time for snorkeling and free diving enthusiasts
  • Why it’s worth it: Apple Watch UltraCMWaterSubmersionManagerProvides native water depth and water temperature APIs without external sensors
  • How to start: Configure the “Shallow Depth and Pressure” capability, implement the delegate to receive depth and temperature updates, and superimpose it into the HealthKit Workout

3. Sports posture analysis tool

  • What: Use 800Hz accelerometer and 200Hz device motion data to analyze golf swings, tennis serves, etc.
  • Why it’s worth it: High-frequency data can capture subtle differences in movements that cannot be distinguished at 100Hz, such as club head speed and wrist flip angle at the moment of impact.
  • How to start: Start HealthKit Workout, useCMBatchedSensorManagerThe async interface obtains data in batches and designs detection algorithms for specific movements.

4. Fitness action counting and quality assessment

  • What to do: Automatically identify the number of completions of push-ups, squats and other movements, and evaluate the quality of the movements
  • Why it’s worth doing: Head tracking data can determine body posture, and high-frequency accelerometers can detect the rhythm and amplitude of movements.
  • How to start: AirPods provide head posture reference,CMBatchedSensorManagerProvides wrist motion details, and the two are combined to build a full-body motion model

Comments

GitHub Issues · utterances