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, are 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 check whether pace information is available. Similarly, you can call the isCadenceAvailable()
class method of CMPedometer
to determine whether cadence information is available.
Import the Core Motion 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
=
Date
(
timeIntervalSinceNow
:
-
(
7
*
24
*
60
*
60
))
pedometer
.
startUpdates
(
from
:
oneWeekAgo
)
{
data
,
error
in
guard
let
pData
=
data
,
error
==
nil
else
{
return
}
if
let
pace
=
pData
.
currentPace
{
(
"Pace =
(
pace
)
"
)
}
if
let
cadence
=
pData
.
currentCadence
{
(
"Cadence =
(
cadence
)
"
)
}
}
// remember to stop the pedometer updates with stopPedometerUpdates()
// at some point
When you finish querying pedometer data, always remember to call the stopPedometerUpdates()
function on your instance of CMPedometer
.
isAccelerometerRecordingAvailable()
class function on CMSensorRecorder
and abort if it returns false
, because that means that accelerometer recording is not available.CMSensorRecorder
.recordAccelerometer(forDuration:)
function on your sensor recorder and pass the number of seconds for which you want to record accelerometer data.accelerometerData(from: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 like 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 Sequence
protocol and implement the makeIterator()
function like so:
extension
CMSensorDataList
:
Sequence
{
public
func
makeIterator
()
->
NSFastEnumerationIterator
{
return
NSFastEnumerationIterator
(
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
.
recordAccelerometer
(
forDuration
:
duration
)
Then I will go to the background and read the data:
OperationQueue
().
addOperation
{[
unowned
recorder
]
in
Thread
.
sleep
(
forTimeInterval
:
duration
)
let
now
=
Date
()
let
past
=
now
.
addingTimeInterval
(
-
(
duration
))
guard
let
data
=
recorder
.
accelerometerData
(
from
:
past
,
to
:
now
)
else
{
return
}
(
data
)
}
It is important to enumerate the result of accelerometerData(from:to:)
on a non-UI thread, because there may be thousands of data points in the results.