diff --git a/example/ios/CmPedometerExample.xcodeproj/project.pbxproj b/example/ios/CmPedometerExample.xcodeproj/project.pbxproj
index cee5920..6b52df2 100644
--- a/example/ios/CmPedometerExample.xcodeproj/project.pbxproj
+++ b/example/ios/CmPedometerExample.xcodeproj/project.pbxproj
@@ -466,6 +466,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 2XU846A9M4;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = CmPedometerExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -493,6 +494,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 2XU846A9M4;
INFOPLIST_FILE = CmPedometerExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
diff --git a/example/ios/CmPedometerExample/Info.plist b/example/ios/CmPedometerExample/Info.plist
index 94d30e4..d22f317 100644
--- a/example/ios/CmPedometerExample/Info.plist
+++ b/example/ios/CmPedometerExample/Info.plist
@@ -45,6 +45,8 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ NSMotionUsageDescription
+ To track your steps.
UIViewControllerBasedStatusBarAppearance
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 847b785..fe8a30c 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
-import { StyleSheet, View, Text } from 'react-native';
+import { StyleSheet, View, Text, Button } from 'react-native';
import {
CMAuthorizationStatus,
authorizationStatus,
@@ -10,63 +10,80 @@ import {
isPaceAvailable,
isCadenceAvailable,
isPedometerEventTrackingAvailable,
+ CMPedometerData,
+ startUpdates,
+ stopUpdates,
} from 'react-native-cm-pedometer';
export default function App() {
- const [authorizationStatusResult, setAuthorizationStatusResult] =
- React.useState();
- const [isStepCountingAvailableResult, setStepCountingAvailableResult] =
- React.useState();
- const [isDistanceAvailableResult, setDistanceAvailableResult] =
- React.useState();
- const [isFloorCountingAvailableResult, setFloorCountingAvailableResult] =
- React.useState();
- const [isPaceAvailableResult, setPaceAvailableResult] = React.useState<
- boolean | undefined
+ const [status, setStatus] = React.useState<
+ CMAuthorizationStatus | undefined
>();
- const [isCadenceAvailableResult, setCadenceAvailableResult] = React.useState<
+ const [isStepAvail, setStepAvail] = React.useState();
+ const [isDistAvail, setDistAvail] = React.useState();
+ const [isFloorAvail, setFloorAvail] = React.useState();
+ const [isPaceAvail, setPaceAvail] = React.useState();
+ const [isCadenceAvail, setCadenceAvail] = React.useState<
boolean | undefined
>();
- const [
- isPedometerEventTrackingAvailableResult,
- setPedometerEventTrackingAvailableResult,
- ] = React.useState();
+ const [isEventAvail, setEventAvail] = React.useState();
React.useEffect(() => {
- authorizationStatus().then(setAuthorizationStatusResult);
- isStepCountingAvailable().then(setStepCountingAvailableResult);
- isDistanceAvailable().then(setDistanceAvailableResult);
- isFloorCountingAvailable().then(setFloorCountingAvailableResult);
- isPaceAvailable().then(setPaceAvailableResult);
- isCadenceAvailable().then(setCadenceAvailableResult);
- isPedometerEventTrackingAvailable().then(
- setPedometerEventTrackingAvailableResult
- );
+ authorizationStatus().then(setStatus);
+ isStepCountingAvailable().then(setStepAvail);
+ isDistanceAvailable().then(setDistAvail);
+ isFloorCountingAvailable().then(setFloorAvail);
+ isPaceAvailable().then(setPaceAvail);
+ isCadenceAvailable().then(setCadenceAvail);
+ isPedometerEventTrackingAvailable().then(setEventAvail);
+ return () => {
+ stopUpdates();
+ };
}, []);
+ const [isDataStarted, setDataStarted] = React.useState(false);
+ const [data, setData] = React.useState();
+ const [error, setError] = React.useState();
+ function onPressData() {
+ if (isDataStarted) {
+ stopUpdates();
+ } else {
+ startUpdates(new Date(), (newError, newData) => {
+ setError(newError);
+ setData(newData);
+ });
+ }
+ setDataStarted(!isDataStarted);
+ }
+
return (
Authorization Status:{' '}
- {authorizationStatusResult !== undefined &&
- CMAuthorizationStatus[authorizationStatusResult]}
-
-
- Is Step Counting Available: {isStepCountingAvailableResult?.toString()}
-
-
- Is Distance Available: {isDistanceAvailableResult?.toString()}
-
-
- Is Floor Counting Available:{' '}
- {isFloorCountingAvailableResult?.toString()}
+ {status !== undefined && CMAuthorizationStatus[status]}
- Is Pace Available: {isPaceAvailableResult?.toString()}
- Is Cadence Available: {isCadenceAvailableResult?.toString()}
+ Is Step Counting Available: {isStepAvail?.toString()}
+ Is Distance Available: {isDistAvail?.toString()}
+ Is Floor Counting Available: {isFloorAvail?.toString()}
+ Is Pace Available: {isPaceAvail?.toString()}
+ Is Cadence Available: {isCadenceAvail?.toString()}
- Is Pedometer Event Tracking Available:{' '}
- {isPedometerEventTrackingAvailableResult?.toString()}
+ Is Pedometer Event Tracking Available: {isEventAvail?.toString()}
+
+ Start Date: {data?.startDate.toLocaleString()}
+ End Date: {data?.endDate.toLocaleString()}
+ Number of Steps: {data?.numberOfSteps}
+ Distance: {data?.distance}
+ Current Cadence: {data?.currentCadence}
+ Current Pace: {data?.currentPace}
+ Average Active Pace: {data?.averageActivePace}
+ Floors Ascended: {data?.floorsAscended}
+ Floors Descended: {data?.floorsDescended}
+ Error: {error?.message}
);
}
diff --git a/ios/CmPedometer.mm b/ios/CmPedometer.mm
index 5a0d17b..233a3e8 100644
--- a/ios/CmPedometer.mm
+++ b/ios/CmPedometer.mm
@@ -1,6 +1,9 @@
#import
+#import
-@interface RCT_EXTERN_MODULE(CmPedometer, NSObject)
+@interface RCT_EXTERN_MODULE(CmPedometer, RCTEventEmitter)
+
+// MARK: - Determining Pedometer Availability
RCT_EXTERN_METHOD(authorizationStatus:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
@@ -23,9 +26,19 @@ @interface RCT_EXTERN_MODULE(CmPedometer, NSObject)
RCT_EXTERN_METHOD(isPedometerEventTrackingAvailable:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
+// MARK: - Gathering Live Pedometer Data
+
+RCT_EXTERN_METHOD(startUpdates:(NSString *)from)
+
+RCT_EXTERN_METHOD(stopUpdates)
+
+// MARK: - Fetching Historical Pedometer Data
+
+// MARK: -
+
+ (BOOL)requiresMainQueueSetup
{
- return NO;
+ return NO;
}
@end
diff --git a/ios/CmPedometer.swift b/ios/CmPedometer.swift
index e9e0d36..0477646 100644
--- a/ios/CmPedometer.swift
+++ b/ios/CmPedometer.swift
@@ -1,39 +1,97 @@
import CoreMotion
+import React
+
+enum CmPedometerEvent: String, CaseIterable {
+ case onPedometerData
+ case onPedometerEvent
+}
@objc(CmPedometer)
-class CmPedometer: NSObject {
+class CmPedometer: RCTEventEmitter {
+ static let instance = CMPedometer()
+ static let dateFormatter = {
+ let dateFormatter = ISO8601DateFormatter()
+ dateFormatter.formatOptions = [
+ .withInternetDateTime,
+ .withFractionalSeconds
+ ]
+ return dateFormatter
+ }()
+
+ // MARK: - Determining Pedometer Availability
+
@objc(authorizationStatus:withRejecter:)
- func authorizationStatus(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func authorizationStatus(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.authorizationStatus().rawValue)
}
@objc(isStepCountingAvailable:withRejecter:)
- func isStepCountingAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isStepCountingAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isStepCountingAvailable())
}
@objc(isDistanceAvailable:withRejecter:)
- func isDistanceAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isDistanceAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isDistanceAvailable())
}
@objc(isFloorCountingAvailable:withRejecter:)
- func isFloorCountingAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isFloorCountingAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isFloorCountingAvailable())
}
@objc(isPaceAvailable:withRejecter:)
- func isPaceAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isPaceAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isPaceAvailable())
}
@objc(isCadenceAvailable:withRejecter:)
- func isCadenceAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isCadenceAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isCadenceAvailable())
}
@objc(isPedometerEventTrackingAvailable:withRejecter:)
- func isPedometerEventTrackingAvailable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
+ func isPedometerEventTrackingAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
resolve(CMPedometer.isPedometerEventTrackingAvailable())
}
+
+ // MARK: - Gathering Live Pedometer Data
+
+ @objc(startUpdates:)
+ func startUpdates(from: String) -> Void {
+ if let from = CmPedometer.dateFormatter.date(from: from) {
+ CmPedometer.instance.startUpdates(from: from) { [weak self] (data, error) in
+ guard let self = self else { return }
+ var body: [String: Any] = [:]
+ body["error"] = error?.localizedDescription
+ if let data = data {
+ body["data"] = [
+ "startDate": CmPedometer.dateFormatter.string(from: data.startDate),
+ "endDate": CmPedometer.dateFormatter.string(from: data.endDate),
+ "numberOfSteps": data.numberOfSteps,
+ "distance": data.distance as Any,
+ "averageActivePace": data.averageActivePace as Any,
+ "currentPace": data.currentPace as Any,
+ "currentCadence": data.currentCadence as Any,
+ "floorsAscended": data.floorsAscended as Any,
+ "floorsDescended": data.floorsDescended as Any,
+ ]
+ }
+ self.sendEvent(withName: CmPedometerEvent.onPedometerData.rawValue, body: body)
+ }
+ }
+ }
+
+ @objc(stopUpdates)
+ func stopUpdates() -> Void {
+ CmPedometer.instance.stopUpdates()
+ }
+
+ // MARK: - Fetching Historical Pedometer Data
+
+ // MARK: -
+
+ override func supportedEvents() -> [String]! {
+ return CmPedometerEvent.allCases.map{ $0.rawValue }
+ }
}
diff --git a/src/index.tsx b/src/index.tsx
index d2edab4..5d4548c 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,4 +1,4 @@
-import { NativeModules, Platform } from 'react-native';
+import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
const LINKING_ERROR =
`The package 'react-native-cm-pedometer' doesn't seem to be linked. Make sure: \n\n` +
@@ -17,6 +17,8 @@ const CmPedometer = NativeModules.CmPedometer
}
);
+// Determining Pedometer Availability
+
export enum CMAuthorizationStatus {
notDetermined,
restricted,
@@ -51,3 +53,56 @@ export function isCadenceAvailable(): Promise {
export function isPedometerEventTrackingAvailable(): Promise {
return CmPedometer.isPedometerEventTrackingAvailable();
}
+
+// Getting Live Pedometer Data
+
+export interface CMPedometerData {
+ startDate: Date;
+ endDate: Date;
+ numberOfSteps: number;
+ distance: number | undefined | null;
+ averageActivePace: number | undefined | null;
+ currentPace: number | undefined | null;
+ currentCadence: number | undefined | null;
+ floorsAscended: number | undefined | null;
+ floorsDescended: number | undefined | null;
+}
+
+const enum CmPedometerEvent {
+ onPedometerData = 'onPedometerData',
+ onPedometerEvent = 'onPedometerEvent',
+}
+
+const eventEmitter = new NativeEventEmitter(CmPedometer);
+
+export function startUpdates(
+ from: Date,
+ withHandler: (
+ error: Error | undefined,
+ data: CMPedometerData | undefined
+ ) => void
+): void {
+ eventEmitter.addListener(CmPedometerEvent.onPedometerData, (event: any) => {
+ let error: Error | undefined;
+ if (event.error) {
+ error = new Error(event.error);
+ }
+ let data: CMPedometerData | undefined;
+ if (event.data) {
+ data = {
+ ...event.data,
+ startDate: new Date(event.data.startDate),
+ endDate: new Date(event.data.endDate),
+ } as CMPedometerData;
+ }
+ withHandler(error, data);
+ });
+ CmPedometer.startUpdates(from.toISOString());
+}
+
+export function stopUpdates(): void {
+ eventEmitter.removeAllListeners(CmPedometerEvent.onPedometerData);
+ CmPedometer.stopUpdates();
+}
+
+// Fetching Historical Pedometer Data