iOS 9 Swift Programming Cookbook (2015)
Chapter 10. Core Motion
10.2 Recording and Reading Accelerometer Data
Problem
You want iOS to accumulate some accelerometer data for a specific number of seconds and then batch update your app with all the accelerometer data in one go.
Solution
Follow these steps:
1. Call the isAccelerometerRecordingAvailable() class function on CMSensorRecorder and abort if it returns false, because that means that accelerometer recording is not available.
2. Instantiate CMSensorRecorder.
3. Call the recordAccelerometerFor(_:) function on your sensor recorder and pass the number of seconds for which you want to record accelerometer data.
4. Go into a background thread and wait for your data if you want.
5. Call the accelerometerDataFrom(_:to:) function on your sensor recorder to get the accelerometer data from a given data to another date. The return value of this function is a CMSensorDataList object, which is enumerable. Each item in this enumeration is of typeCMRecordedAccelerometerData.
6. Read the value of each CMRecordedAccelerometerData. You’ll have properties such as startDate, timestamp, and acceleration, which is of type CMAcceleration.
Discussion
I mentioned that CMSensorDataList is enumerable. That means it conforms to the NSFastEnumeration protocol, but you can not use the for x in ... syntax on this type of enumerable object. You’ll have to make it conform to the SequenceType protocol and implement thegenerate() function like so:
extension CMSensorDataList : SequenceType{
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
So I’m going to first define a lazily allocated sensor recorder. If sensor information is not available, my object won’t hang around in the memory:
lazy var recorder = CMSensorRecorder()
Then I check whether sensor information is available:
guard CMSensorRecorder.isAccelerometerRecordingAvailable() else {
print("Accelerometer data recording is not available")
return
}
Next I will record my sensor data for a period:
let duration = 3.0
recorder.recordAccelerometerFor(duration)
Then I will go to background and read the data:
NSOperationQueue().addOperationWithBlock{[unowned recorder] in
NSThread.sleepForTimeInterval(duration)
let now = NSDate()
let past = now.dateByAddingTimeInterval(-(duration))
let data = recorder.accelerometerDataFrom(past, to: now)
let accelerationData: [CMAcceleration] = data.map{
//every $0 is CMRecordedAccelerometerData
$0.acceleration
}
print(accelerationData)
}
NOTE
It is important to enumerate the result of accelerometerDataFrom(_:to:) on a non-UI thread, because there may be thousands of data points in the results.
See Also