Skip to content

Commit

Permalink
docs(react): 📝 wrote a detailed document on importing turbo-module
Browse files Browse the repository at this point in the history
We have written a detailed document on
importing turbo-module-built libraries
into existing legacy reactive applications.
The *.config.js configuration files that
were all merged into package.json were
separated into separate files.

We need detailed documentation to persuade others. #10
  • Loading branch information
AndrewDongminYoo committed Mar 14, 2023
1 parent 967b52a commit 8d8800a
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 222 deletions.
8 changes: 4 additions & 4 deletions .trunk/trunk.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
version: 0.1
cli:
version: 1.5.1
version: 1.6.0
plugins:
sources:
- id: trunk
ref: v0.0.11
ref: v0.0.13
uri: https://github.com/trunk-io/plugins
lint:
disabled:
Expand All @@ -14,14 +14,14 @@ lint:
- git-diff-check
- gitleaks
enabled:
- swiftformat@0.50.9
- swiftformat@0.51.2
- swiftlint@0.50.3
- yamllint@1.29.0
- dotenv-linter@3.3.0
- markdownlint@0.33.0
- shellcheck@0.9.0
- prettier@2.8.4
- eslint@8.34.0
- eslint@8.35.0
- actionlint@1.6.23
ignore:
- linters:
Expand Down
241 changes: 142 additions & 99 deletions README.kr.md

Large diffs are not rendered by default.

232 changes: 136 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,129 @@
# React-Native Step Counter Library

한국어 사용자 분들은 [Korean version.](README.kr.md)를 참조하십시오.
한국어 사용자는 [Korean version.](README.kr.md)를 참조하십시오.

A simple React Native package to count the number of steps taken by the user. This package uses the StepCounter (or Accelerometer) Sensor API on Android and the Core Motion framework on iOS to count the steps.
A simple React Native package to count the number of steps taken by the user. This package uses the `StepCounter` (or Custom accelerometer-based stepcounter) Sensor API on Android and the `Core Motion` framework on iOS to count the steps. It's built using Turbo Module, a new module development architecture for React Native. I made this library compatible with both new and legacy architectures. (Because the turbo module is still in the experimental stage. so it is not widely used.)

## Installation

```zsh
```shell
# if you use pure npm (what a classic!),
npm install @dongminyu/react-native-step-counter
```

# or if you use Yarn,
```shell
# or if you prefer to use Yarn (I love it's parallel install feature),
yarn add @dongminyu/react-native-step-counter
```

## Requirements
```shell
# or if you use pnpm (it's fast and efficient),
pnpm add @dongminyu/react-native-step-counter
```

Native modules will automatically connect after React Native 0.60 version. So you don't need to link the native modules manually.

👣 if you are using the legacy architecture, you need to follow the guide below. otherwise, you can [skip](README.md#android) next step.

## Setup the New Architecture

- Applying a new architecture to React Native applications (Common)

1. React Native released the support for the New Architecture with the release [`0.68.0`](https://reactnative.dev/blog/2022/03/30/version-068#opting-in-to-the-new-architecture).
2. This is written with the expectation that you’re using the [latest React Native release](https://github.com/facebook/react-native/releases/latest).
3. You can find instructions on how to upgrade in the [page upgrading to new versions](https://reactnative.dev/docs/upgrading).
4. write all JS bridges with [TypeScript](https://www.typescriptlang.org/) (or [Flow.js](https://flow.org/)) because Codegen requires explicitly defined types. As you know, JavaScript is a dynamically typed language, so it is not possible to generate code.
5. use hermes and flipper debugging tools.
- [Hermes](https://reactnative.dev/docs/hermes) is a new JavaScript engine optimized for running React Native apps on Android and iOS. enabled by default, and you want to use JSC, explicitly disable it.
- [Flipper](https://fbflipper.com/) is a new debugging and profiling tool for React Native.

- Applying a new architecture to React Native iOS applications

_1_. set platform version to [12.4](https://github.com/facebook/react-native/blob/main/CHANGELOG.md#ios-specific-25) or higher.

```diff
- platform :ios, '11.0'
+ platform :ios, '12.4'
```

_2_. set NODE_BINARY to .xcode.env file.

```shell
echo 'export NODE_BINARY=$(command -v node)' > .xcode.env
```

_3_. Fix an API Change in the `AppDelegate.m` file.

```diff
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
- return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
```

_4_. Rename all Objective-C(.m) files to Objective-C++ (.mm)
_5_. Make your AppDelegate conform to RCTAppDelegate

- [ios/StepCounterExample/AppDelegate.h](https://github.com/AndrewDongminYoo/react-native-step-counter/blob/main/example/ios/StepCounterExample/AppDelegate.h)

```diff
- #import <React/RCTBridgeDelegate.h>
+ #import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>

- @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
+ @interface AppDelegate : RCTAppDelegate

- @property (nonatomic, strong) UIWindow *window;
@end
```

- [ios/StepCounterExample/AppDelegate.mm](https://github.com/AndrewDongminYoo/react-native-step-counter/blob/main/example/ios/StepCounterExample/AppDelegate.mm)
```objective-c++
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"StepCounterExample";
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
- (BOOL)concurrentRootEnabled
{
return true;
}
@end
```
- Run `pod install`
```shell
export RCT_NEW_ARCH_ENABLED=1
cd ios && pod install
```

- Applying a new architecture to React Native Android applications
1. If your project has React Native later than `v0.71.0`, you already meet all the prerequisites to use the New Architecture on Android.
2. You will only need to set `newArchEnabled` to `true` in your `android/gradle.properties` file.

> If you prefer to read the official documentation, you can find it [here](https://reactnative.dev/docs/new-architecture-intro).

### ANDROID

> 3 uses-permission, 3 uses-feature

```xml
<!-- android/src/main/AndroidManifest.xml-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
Expand All @@ -39,6 +146,8 @@ yarn add @dongminyu/react-native-step-counter

### iOS

> set NSMotionUsageDescription (+NSHealthUpdateUsageDescription)
```xml plist
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN">
<plist version="1.0">
Expand All @@ -53,32 +162,36 @@ yarn add @dongminyu/react-native-step-counter

## Interface

- `isStepCountingSupported(): Promise<Record<string, boolean>>`: method to check if the device has a step counter or accelerometer sensor.
- `isStepCountingSupported()`: Promise<Record<string, boolean>>: method to check if the device has a feature related step counter or accelerometer.

- request permission for the required sensors and check if the device has a step counter or accelerometer sensor.
- returns true if the device has a step counter or accelerometer sensor. (usually true for Android devices)
- One key for the response object is `granted`, whether the app user has granted this feature permission, and `supported` is whether the device supports this feature.
- This NativeModule can apply algorithms to a raw accelerometer to extract walking event data without activity sensor privileges, regardless of this response, but it is not recommended. You must write a code that stops tracking sensor events if user denies read-permission - even if you can do that.

- `startStepCounterUpdate(start: Date, callBack: StepCountUpdateCallback): EmitterSubscription`:
- `startStepCounterUpdate(start: Date, callBack: StepCountUpdateCallback)`: EmitterSubscription:

- `stepSensor` is set to step counter sensor or accelerometer sensor.
- if step counter sensor is found, then register as listener.
- else if accelerometer sensor is found instead, then register as listener.
- create instance of sensor event listener for `stepSensor` to receive sensor events.
- Set the appropriate listening delay depending on which type of sensor.
- if step counter sensor is found, then set delay to Sensor.DELAY_NORMAL.
- if accelerometer sensor is found instead, then set delay to Sensor.DELAY_FASTEST.
- If the pedometer sensor is available and supported on the device, register it with the listener in the sensor manager, and return the step count event listener.
- If the pedometer sensor is not supported by the device or is not available, register the accelerometer sensor with the listener, generate a accel event through an vector algorithm filter and receive it to the app.

- `stopStepCounterUpdate(): void`:

- unregister the listener from `sensorManager`.
- unregister registered listener from `sensorManager` and release it.

- `StepCountData`:

- **Common Interface**

- `steps`: This is a number property that indicates the number of steps taken by the user during the specified time period.
- `startDate`: This is a number property that indicates the start date of the data in Unix timestamp format, measured in milliseconds.
- `endDate`: This is a number property that indicates the end date of the data in Unix timestamp format, measured in milliseconds.
- `distance`: This is a number property that indicates the distance in meters that the user has walked or run during the specified time period.
- `counterType`: The type of sensor used to count the steps. This property can have one of two values: `STEP_COUNTER` or `ACCELEROMETER` or `CMPedometer`.
- `counterType`: The name of the sensor used to count the number of steps. In iOS, only the `CMPedometer` is returned, and in Android, the `StepCounter` or `Accelerometer` is returned depending on the device state.

- **iOS Only**
- `floorsAscended`: This is a number property that indicates the number of floors the user has ascended during the specified time period. it can be nil if the device does not support this feature.
- `floorsDescended`: This is a number property that indicates the number of floors the user has descended during the specified time period. it can be nil if the device does not support this feature.
- `currentPace`: (iOS 9.0+) This is a number property that indicates the current pace of the user in meters per second.
- `currentCadence`: (iOS 9.0+) This is a number property that indicates the current cadence of the user in steps per second.
- `averageActivePace`: (iOS 10.0+) This is a number property that indicates the average pace of the user in meters per second.

## Usage

Expand Down Expand Up @@ -124,95 +237,22 @@ async function startStepCounter() {
}
```

Here's an example of a complete React component that uses the `NativeStepCounter`:
Here's an example of a complete React component that uses the `NativeStepCounter`.

```typescript
export default function App() {
const [supported, setSupported] = useState(false);
const [granted, setGranted] = useState(false);
const [steps, setSteps] = useState(0);

/** get user's motion permission and check pedometer is available */
async function askPermission() {
isStepCountingSupported().then((result) => {
console.debug('🚀 - isStepCountingSupported', result);
setGranted(result.granted === true);
setSupported(result.supported === true);
});
}

async function startStepCounter() {
startStepCounterUpdate(new Date(), (data) => {
console.debug(parseStepData(data));
setSteps(data.steps);
});
}

function stopStepCounter() {
setSteps(0);
stopStepCounterUpdate();
}

useEffect(() => {
console.debug('🚀 - componentDidMount');
askPermission();
return () => {
console.debug('🚀 - componentWillUnmount');
stopStepCounter();
};
}, []);

useEffect(() => {
console.debug('🚀 - componentDidUpdate');
if (granted && supported) {
console.debug('🚀 - granted and supported');
startStepCounter();
} else if (granted && !supported) {
console.debug('🚀 - granted but not supported');
startStepCounter();
} else if (supported && !granted) {
console.debug('🚀 - supported but not granted');
requestPermission().then((accepted) => {
console.debug('🚀 - requestPermission', accepted);
setGranted(accepted);
});
}
}, [granted, supported]);

return (
<SafeAreaView>
<View style={styles.container}>
<Text style={styles.normText}>User Granted Step Counter Feature?: {granted ? 'yes' : 'no'}</Text>
<Text style={styles.normText}>Device has Step Counter Sensor?: {supported ? 'yes' : 'no'}</Text>
{!granted ? (
<>
<Button title="Request Permission Again" onPress={requestPermission} />
</>
) : (
<>
<Text style={styles.normText}>걸음 수: {steps}</Text>
<Button title="Start StepCounter Updates" onPress={startStepCounter} />
<Button title="Stop StepCounter Updates" onPress={stopStepCounter} />
</>
)}
</View>
</SafeAreaView>
);
}
```
Link to Example Application: [here](https://github.com/AndrewDongminYoo/react-native-step-counter/blob/main/example/src/App.tsx)

## Change Log

See the [CHANGELOG](CHANGELOG.md) for a list of changes.
See the [`Release Notes`](CHANGELOG.md) for a list of changes.

## Contributing

See the [Contributing Guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
See the [`Contributing Guide`](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

## License

MIT

---

Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob/tree/main/packages/create-react-native-library) by [CallStack](https://callstack.com/).
2 changes: 1 addition & 1 deletion RNStepCounter.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.license = package["license"]
s.authors = package["author"]
s.description = package["description"]
s.platforms = { :ios => "11.0" }
s.platforms = { :ios => "13.0" }
s.source = { :git => "https://github.com/AndrewDongminYoo/react-native-step-counter.git", :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm,swift}"
Expand Down
14 changes: 14 additions & 0 deletions bob.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
source: 'src',
output: 'lib',
targets: [
'commonjs',
'module',
[
'typescript',
{
project: 'tsconfig.build.json',
},
],
],
};
2 changes: 1 addition & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
# @see
# @see https://reactnative.dev/docs/new-architecture-app-intro
# TODO: Remove this flag once we have a stable release of Fabric.
newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine.
Expand Down
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
preset: 'react-native',
modulePathIgnorePatterns: ['<rootDir>/example/node_modules', '<rootDir>/lib/'],
};
21 changes: 0 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,27 +131,6 @@
"node": ">= 16.0.0"
},
"packageManager": "^yarn@1.22.19",
"jest": {
"preset": "react-native",
"modulePathIgnorePatterns": [
"<rootDir>/example/node_modules",
"<rootDir>/lib/"
]
},
"react-native-builder-bob": {
"source": "src",
"output": "lib",
"targets": [
"commonjs",
"module",
[
"typescript",
{
"project": "tsconfig.build.json"
}
]
]
},
"codegenConfig": {
"name": "RNStepCounterSpec",
"type": "modules",
Expand Down

0 comments on commit 8d8800a

Please sign in to comment.