This year, Apple finally brought some long-awaited features into the Core Motion framework. It’s especially exciting that the same capabilities, or some version of them, is also available on the Apple Watch. This is great news for us developers because we can program for the watch in a more native way, rather than reading this data from the user’s iPhone and sending it to the watch with Bluetooth.
There are a couple key terms I’ll be using throughout this chapter that you need to know about:
iOS devices can provide pace and cadence information when it’s available from the pedometer. Some pedometers might not have this information available. You can call the isPaceAvailable()
class function of CMPedometer
to find out whether pace information is available. Similarly, the isCadenceAvailable()
class method of CMPedometer
can tell you whether cadence information is available.
Import the CoreMotion framework into your project before attempting to run the code we write in this chapter.
Let’s check out an example:
guard
CMPedometer
.
isCadenceAvailable
()
&&
CMPedometer
.
isPaceAvailable
()
else
{
(
"Pace and cadence data are not available"
)
return
}
let
oneWeekAgo
=
NSDate
(
timeIntervalSinceNow
:
-
(
7
*
24
*
60
*
60
))
pedometer
.
startPedometerUpdatesFromDate
(
oneWeekAgo
)
{
data
,
error
in
guard
let
pData
=
data
where
error
==
nil
else
{
return
}
if
let
pace
=
pData
.
currentPace
{
(
"Pace = (pace)"
)
}
if
let
cadence
=
pData
.
currentCadence
{
(
"Cadence = (cadence)"
)
}
}
isAccelerometerRecordingAvailable()
class function on CMSensorRecorder
and abort if it returns false
, because that means that accelerometer recording is not availableCMSensorRecorder.
recordAccelerometerFor(_:)
function on your sensor recorder and pass the number of seconds for which you want to record accelerometer data.accelerometerDataFrom(_:to:)
function on your sensor recorder to get the accelerometer data from a given date to another date. The return value of this function is a CMSensorDataList
object, which is enumerable. Each item in this enumeration is of type CMRecordedAccelerometerData
.CMRecordedAccelerometerData
. You’ll have properties such as startDate
, timestamp
, and acceleration
, which is of type CMAcceleration
.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 the generate()
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
{
(
"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 the background and read the data:
NSOperationQueue
().
addOperationWithBlock
{[
unowned
recorder
]
in
NSThread
.
sleepForTimeInterval
(
duration
)
let
now
=
NSDate
()
let
past
=
now
.
dateByAddingTimeInterval
(
-
(
duration
))
guard
let
data
=
recorder
.
accelerometerDataFrom
(
past
,
to
:
now
)
else
{
return
}
let
accelerationData
:
[
CMAcceleration
]
=
data
.
map
{
//every $0 is CMRecordedAccelerometerData
$
0.
acceleration
}
(
accelerationData
)
}
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.