Skip to content
All essays
iOSMarch 27, 202512 min

Swift Core Motion: Sensor Data and Motion

Access device sensors with Core Motion. Accelerometer, gyroscope, and device motion.

Ü
Ümit Uz
Mobile & Full Stack Developer

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

  1. 1Battery: Minimize sensor usage
  2. 2Permissions: Request appropriate permissions
  3. 3Updates: Use appropriate update intervals
  4. 4Filtering: Filter sensor noise
  5. 5Calibration: Calibrate when needed
  6. 6Performance: Optimize for performance
  7. 7Testing: Test on real devices
  8. 8UX: Provide clear visual feedback

Core Motion enables motion-aware and sensor-rich applications!

Related essays

Next essay
Swift PassKit: Apple Wallet Integration