Dpearson2699 core motion
Skill JordanCoin/ios-skills-collection/skills/dpearson2699--core-motion
200+ iOS/Swift/Xcode agent skills from 28 authors with phase-aware routing. Claude Code plugin + Codex CLI + Mac App. Full lifecycle: ideation → design → develop → test → deploy → iterate.
npx -y skills add JordanCoin/ios-skills-collection --skill dpearson2699--core-motionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Access accelerometer, gyroscope, magnetometer, pedometer, and activity-recognition data using CoreMotion. Use when reading device sensor data, counting steps, detecting user activity (walking/running/driving), tracking altitude changes, or implementing motion-based interactions in iOS/watchOS apps.
SKILL.md
11.5 KB, as published. Nobody here has run it
CoreMotion
Read device sensor data -- accelerometer, gyroscope, magnetometer, pedometer, and activity recognition -- on iOS and watchOS. CoreMotion fuses raw sensor inputs into processed device-motion data and provides pedometer/activity APIs for fitness and navigation use cases. Targets Swift 6.3 / iOS 26+.
Contents
- Setup
- CMMotionManager: Sensor Data
- Processed Device Motion
- CMPedometer: Step and Distance Data
- CMMotionActivityManager: Activity Recognition
- CMAltimeter: Altitude Data
- Update Intervals and Battery
- Common Mistakes
- Review Checklist
- References
Setup
Info.plist
Add NSMotionUsageDescription to Info.plist with a user-facing string explaining
why your app needs motion data. Without this key, the app crashes on first access.
<key>NSMotionUsageDescription</key>
<string>This app uses motion data to track your activity.</string>
Authorization
CoreMotion uses CMAuthorizationStatus for pedometer and activity APIs. Sensor
APIs (accelerometer, gyro) do not require explicit authorization but do require
the usage description key.
import CoreMotion
let status = CMMotionActivityManager.authorizationStatus()
switch status {
case .notDetermined:
// Will prompt on first use
break
case .authorized:
break
case .restricted, .denied:
// Direct user to Settings
break
@unknown default:
break
}
CMMotionManager: Sensor Data
Create exactly one CMMotionManager per app. Multiple instances degrade
sensor update rates.
import CoreMotion
let motionManager = CMMotionManager()
Accelerometer Updates
guard motionManager.isAccelerometerAvailable else { return }
motionManager.accelerometerUpdateInterval = 1.0 / 60.0 // 60 Hz
motionManager.startAccelerometerUpdates(to: .main) { data, error in
guard let acceleration = data?.acceleration else { return }
print("x: \(acceleration.x), y: \(acceleration.y), z: \(acceleration.z)")
}
// When done:
motionManager.stopAccelerometerUpdates()
Gyroscope Updates
guard motionManager.isGyroAvailable else { return }
motionManager.gyroUpdateInterval = 1.0 / 60.0
motionManager.startGyroUpdates(to: .main) { data, error in
guard let rotationRate = data?.rotationRate else { return }
print("x: \(rotationRate.x), y: \(rotationRate.y), z: \(rotationRate.z)")
}
motionManager.stopGyroUpdates()
Polling Pattern (Games)
For games, start updates without a handler and poll the latest sample each frame:
motionManager.startAccelerometerUpdates()
// In your game loop / display link:
if let data = motionManager.accelerometerData {
let tilt = data.acceleration.x
// Move player based on tilt
}
Processed Device Motion
Device motion fuses accelerometer, gyroscope, and magnetometer into a single
CMDeviceMotion object with attitude, user acceleration (gravity removed),
rotation rate, and calibrated magnetic field.
guard motionManager.isDeviceMotionAvailable else { return }
motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
motionManager.startDeviceMotionUpdates(
using: .xArbitraryZVertical,
to: .main
) { motion, error in
guard let motion else { return }
let attitude = motion.attitude // roll, pitch, yaw
let userAccel = motion.userAcceleration
let gravity = motion.gravity
let heading = motion.heading // 0-360 degrees (requires magnetometer)
print("Pitch: \(attitude.pitch), Roll: \(attitude.roll)")
}
motionManager.stopDeviceMotionUpdates()
Attitude Reference Frames
| Frame | Use Case |
|---|---|
.xArbitraryZVertical | Default. Z is vertical, X arbitrary at start. Most games. |
.xArbitraryCorrectedZVertical | Same as above, corrected for gyro drift over time. |
.xMagneticNorthZVertical | X points to magnetic north. Requires magnetometer. |
.xTrueNorthZVertical | X points to true north. Requires magnetometer + location. |
Check available frames before use:
let available = CMMotionManager.availableAttitudeReferenceFrames()
if available.contains(.xTrueNorthZVertical) {
// Safe to use true north
}
CMPedometer: Step and Distance Data
CMPedometer provides step counts, distance, pace, cadence, and floor counts.
let pedometer = CMPedometer()
guard CMPedometer.isStepCountingAvailable() else { return }
// Historical query
pedometer.queryPedometerData(
from: Calendar.current.startOfDay(for: Date()),
to: Date()
) { data, error in
guard let data else { return }
print("Steps today: \(data.numberOfSteps)")
print("Distance: \(data.distance?.doubleValue ?? 0) meters")
print("Floors up: \(data.floorsAscended?.intValue ?? 0)")
}
// Live updates
pedometer.startUpdates(from: Date()) { data, error in
guard let data else { return }
print("Steps: \(data.numberOfSteps)")
}
// Stop when done
pedometer.stopUpdates()
Availability Checks
| Method | What It Checks |
|---|---|
isStepCountingAvailable() | Step counter hardware |
isDistanceAvailable() | Distance estimation |
isFloorCountingAvailable() | Barometric altimeter for floors |
isPaceAvailable() | Pace data |
isCadenceAvailable() | Cadence data |
CMMotionActivityManager: Activity Recognition
Detects whether the user is stationary, walking, running, cycling, or in a vehicle.
let activityManager = CMMotionActivityManager()
guard CMMotionActivityManager.isActivityAvailable() else { return }
// Live activity updates
activityManager.startActivityUpdates(to: .main) { activity in
guard let activity else { return }
if activity.walking {
print("Walking (confidence: \(activity.confidence.rawValue))")
} else if activity.running {
print("Running")
} else if activity.automotive {
print("In vehicle")
} else if activity.cycling {
print("Cycling")
} else if activity.stationary {
print("Stationary")
}
}
activityManager.stopActivityUpdates()
Historical Activity Query
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
activityManager.queryActivityStarting(
from: yesterday,
to: Date(),
to: .main
) { activities, error in
guard let activities else { return }
for activity in activities {
print("\(activity.startDate): walking=\(activity.walking)")
}
}
CMAltimeter: Altitude Data
let altimeter = CMAltimeter()
guard CMAltimeter.isRelativeAltitudeAvailable() else { return }
altimeter.startRelativeAltitudeUpdates(to: .main) { data, error in
guard let data else { return }
print("Relative altitude: \(data.relativeAltitude) meters")
print("Pressure: \(data.pressure) kPa")
}
altimeter.stopRelativeAltitudeUpdates()
For absolute altitude (GPS-based):
guard CMAltimeter.isAbsoluteAltitudeAvailable() else { return }
altimeter.startAbsoluteAltitudeUpdates(to: .main) { data, error in
guard let data else { return }
print("Altitude: \(data.altitude)m, accuracy: \(data.accuracy)m")
}
altimeter.stopAbsoluteAltitudeUpdates()
Update Intervals and Battery
| Interval | Hz | Use Case | Battery Impact |
|---|---|---|---|
1.0 / 10.0 | 10 | UI orientation | Low |
1.0 / 30.0 | 30 | Casual games | Moderate |
1.0 / 60.0 | 60 | Action games | High |
1.0 / 100.0 | 100 | Max rate (iPhone) | Very High |
Use the lowest frequency that meets your needs. CMMotionManager caps at 100 Hz
per sample. For higher frequencies, use CMBatchedSensorManager on watchOS/iOS 17+.
Common Mistakes
DON'T: Create multiple CMMotionManager instances
// WRONG -- degrades update rates for all instances
class ViewA { let motion = CMMotionManager() }
class ViewB { let motion = CMMotionManager() }
// CORRECT -- single instance, shared across the app
@Observable
final class MotionService {
static let shared = MotionService()
let manager = CMMotionManager()
}
DON'T: Skip sensor availability checks
// WRONG -- crashes on devices without gyroscope
motionManager.startGyroUpdates(to: .main) { data, _ in }
// CORRECT -- check first
guard motionManager.isGyroAvailable else {
showUnsupportedMessage()
return
}
motionManager.startGyroUpdates(to: .main) { data, _ in }
DON'T: Forget to stop updates
// WRONG -- updates keep running, draining battery
class MotionVC: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
motionManager.startAccelerometerUpdates(to: .main) { _, _ in }
}
// Missing viewDidDisappear stop!
}
// CORRECT -- stop in the counterpart lifecycle method
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
motionManager.stopAccelerometerUpdates()
}
DON'T: Use unnecessarily high update rates
// WRONG -- 100 Hz for a compass display
motionManager.deviceMotionUpdateInterval = 1.0 / 100.0
// CORRECT -- 10 Hz is more than enough for a compass
motionManager.deviceMotionUpdateInterval = 1.0 / 10.0
DON'T: Assume all CMMotionActivity properties are mutually exclusive
// WRONG -- checking only one property
if activity.walking { handleWalking() }
// CORRECT -- multiple can be true simultaneously; check confidence
if activity.walking && activity.confidence == .high {
handleWalking()
} else if activity.automotive && activity.confidence != .low {
handleDriving()
}
Review Checklist
-
NSMotionUsageDescriptionpresent in Info.plist with a clear explanation - Single
CMMotionManagerinstance shared across the app - Sensor availability checked before starting updates (
isAccelerometerAvailable, etc.) - Authorization status checked before pedometer/activity APIs
- Update interval set to the lowest acceptable frequency
- All
start*Updatescalls have matchingstop*Updatesin lifecycle counterparts - Handlers dispatched to appropriate queues (not blocking main for heavy processing)
-
CMMotionActivity.confidencechecked before acting on activity type - Error parameters checked in update handlers
- Attitude reference frame chosen based on actual need (not defaulting to true north unnecessarily)
References
- Extended patterns (SwiftUI integration, batched sensor manager, headphone motion): references/motion-patterns.md
- CoreMotion framework
- CMMotionManager
- CMPedometer
- CMMotionActivityManager
- CMDeviceMotion
- CMAltimeter
- CMBatchedSensorManager
- Getting processed device-motion data