Core Motion provides access to device motion and orientation data. Learn to work with accelerometer, gyroscope, and other sensors.
Motion Manager Setup
Initialize Motion Manager
swift
import CoreMotion
import SwiftUI
class MotionManager: ObservableObject {
private let motionManager = CMMotionManager()
@Published var accelerometerData: CMAccelerometerData?
@Published var gyroData: CMGyroData?
@Published var deviceMotion: CMDeviceMotion?
init() {
setupMotionUpdates()
}
func setupMotionUpdates() {
// Accelerometer
if motionManager.isAccelerometerAvailable {
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdates(to: .main) { [weak self] data, error in
self?.accelerometerData = data
}
}
// Gyroscope
if motionManager.isGyroAvailable {
motionManager.gyroUpdateInterval = 0.1
motionManager.startGyroUpdates(to: .main) { [weak self] data, error in
self?.gyroData = data
}
}
// Device Motion
if motionManager.isDeviceMotionAvailable {
motionManager.deviceMotionUpdateInterval = 0.1
motionManager.startDeviceMotionUpdates(to: .main) { [weak self] data, error in
self?.deviceMotion = data
}
}
}
func stopUpdates() {
motionManager.stopAccelerometerUpdates()
motionManager.stopGyroUpdates()
motionManager.stopDeviceMotionUpdates()
}
}Accelerometer
Accelerometer Data
swift
struct AccelerometerView: View {
@StateObject private var motionManager = MotionManager()
var body: some View {
VStack(spacing: 20) {
Text("Accelerometer")
.font(.title)
if let data = motionManager.accelerometerData {
HStack {
VStack {
Text("X")
.font(.headline)
Text(String(format: "%.2f", data.acceleration.x))
.font(.title)
}
VStack {
Text("Y")
.font(.headline)
Text(String(format: "%.2f", data.acceleration.y))
.font(.title)
}
VStack {
Text("Z")
.font(.headline)
Text(String(format: "%.2f", data.acceleration.z))
.font(.title)
}
}
}
}
.onAppear {
motionManager.setupMotionUpdates()
}
.onDisappear {
motionManager.stopUpdates()
}
}
}Shake Detection
swift
class ShakeDetector: ObservableObject {
@Published var isShaken = false
private let motionManager = CMMotionManager()
func startDetection() {
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates(to: .main) { [weak self] data, error in
guard let data = data else { return }
let acceleration = sqrt(
pow(data.acceleration.x, 2) +
pow(data.acceleration.y, 2) +
pow(data.acceleration.z, 2)
)
if acceleration > 2.5 {
self?.isShaken = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self?.isShaken = false
}
}
}
}
func stopDetection() {
motionManager.stopAccelerometerUpdates()
}
}Gyroscope
Rotation Rate
swift
class GyroscopeManager: ObservableObject {
@Published var rotationRate: CMRotationRate?
private let motionManager = CMMotionManager()
func startGyroscope() {
guard motionManager.isGyroAvailable else {
print("Gyroscope not available")
return
}
motionManager.gyroUpdateInterval = 0.1
motionManager.startGyroUpdates(to: .main) { [weak self] data, error in
self?.rotationRate = data?.rotationRate
}
}
func stopGyroscope() {
motionManager.stopGyroUpdates()
}
var totalRotation: Double {
guard let rate = rotationRate else { return 0 }
return sqrt(pow(rate.x, 2) + pow(rate.y, 2) + pow(rate.z, 2))
}
}Device Orientation
Orientation Tracking
swift
class OrientationManager: ObservableObject {
@Published var orientation: UIDeviceOrientation = .portrait
init() {
startTracking()
}
func startTracking() {
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(
forName: UIDevice.orientationDidChangeNotification,
object: UIDevice.current,
queue: .main
) { [weak self] _ in
self?.orientation = UIDevice.current.orientation
}
}
func stopTracking() {
UIDevice.current.endGeneratingDeviceOrientationNotifications()
}
}Pedometer
Step Counting
swift
import CoreMotion
class PedometerManager: ObservableObject {
@Published var stepCount: Int = 0
@Published var distance: Double = 0
private let pedometer = CMPedometer()
func startStepCounting() {
guard CMPedometer.isStepCountingAvailable() else {
print("Step counting not available")
return
}
pedometer.startUpdates(from: Date()) { [weak self] data in
DispatchQueue.main.async {
self?.stepCount = data.numberOfSteps.intValue
self?.distance = data.distance?.doubleValue ?? 0
}
}
}
func stopStepCounting() {
pedometer.stopUpdates()
}
func queryStepsSince(_ date: Date) {
pedometer.queryStepCountStarting(from: date, to: Date()) { data, error in
if let data = data {
DispatchQueue.main.async {
self.stepCount = data.numberOfSteps.intValue
}
}
}
}
}Magnetometer
Compass Heading
swift
class MagnetometerManager: ObservableObject {
@Published var heading: Double = 0
private let motionManager = CMMotionManager()
func startMagnetometer() {
if motionManager.isMagnetometerAvailable {
motionManager.magnetometerUpdateInterval = 0.1
motionManager.startMagnetometerUpdates(to: .main) { [weak self] data, error in
guard let data = data else { return }
let magneticField = data.magneticField
let magnitude = sqrt(
pow(magneticField.x, 2) +
pow(magneticField.y, 2) +
pow(magneticField.z, 2)
)
self?.heading = Double(atan2(magneticField.y, magneticField.x))
}
}
}
func stopMagnetometer() {
motionManager.stopMagnetometerUpdates()
}
}Altimeter
Altitude Tracking
swift
import CoreMotion
class AltimeterManager: ObservableObject {
@Published var altitude: Double = 0
@Published var pressure: Double = 0
private let altimeter = CMAltimeter()
func startAltimeter() {
guard CMAltimeter.isRelativeAltitudeAvailable() else {
print("Altimeter not available")
return
}
altimeter.startRelativeAltitudeUpdates(to: .main) { [weak self] data, error in
guard let data = data else { return }
DispatchQueue.main.async {
self?.altitude = data.relativeAltitude.doubleValue
self?.pressure = data.pressure.doubleValue
}
}
}
func stopAltimeter() {
altimeter.stopRelativeAltitudeUpdates()
}
}Motion Activity
Activity Recognition
swift
import CoreMotion
class ActivityManager: ObservableObject {
@Published var currentActivity: String = "Unknown"
private let activityManager = CMMotionActivityManager()
func startActivityUpdates() {
activityManager.startActivityUpdates(to: .main) { [weak self] activity in
guard let activity = activity else { return }
var activityString = "Unknown"
if activity.walking {
activityString = "Walking"
} else if activity.running {
activityString = "Running"
} else if activity.automotive {
activityString = "Driving"
} else if activity.stationary {
activityString = "Stationary"
} else if activity.cycling {
activityString = "Cycling"
}
DispatchQueue.main.async {
self?.currentActivity = activityString
}
}
}
func stopActivityUpdates() {
activityManager.stopActivityUpdates()
}
func queryHistoricalActivity(from start: Date, to end: Date) {
let query = CMMotionActivityQuery()
query.from = start
query.to = end
activityManager.queryHistoricalActivity(with: query) { activities, error in
if let activities = activities {
for activity in activities {
print("Activity: \(activity)")
}
}
}
}
}Best Practices
- 1Battery: Minimize sensor usage
- 2Permissions: Request appropriate permissions
- 3Updates: Use appropriate update intervals
- 4Filtering: Filter sensor noise
- 5Calibration: Calibrate when needed
- 6Performance: Optimize for performance
- 7Testing: Test on real devices
- 8UX: Provide clear visual feedback
Core Motion enables motion-aware and sensor-rich applications!