Thank you for your interest in contributing to react-native-ble-nitro! This document provides guidelines and information for contributors.
- Code of Conduct
- Getting Started
- Development Setup
- Architecture Overview
- Development Workflow
- Testing
- Code Standards
- Submitting Changes
- Nitro Development
- Native Development
This project follows a code of conduct to ensure a welcoming environment for all contributors. Please be respectful, inclusive, and professional in all interactions.
- Node.js (v22 or higher)
- npm
- React Native development environment (iOS/Android)
- Xcode (for iOS development)
- Android Studio (for Android development)
- TypeScript knowledge
- Nitro Modules familiarity (helpful)
react-native-ble-nitro/
├── src/ # TypeScript source code
│ ├── specs/ # Nitro module specifications
│ ├── utils/ # Utility functions
│ └── __tests__/ # Unit tests
├── ios/ # iOS native implementation (Swift)
├── android/ # Android native implementation (Kotlin)
├── plugin/ # Expo config plugin
├── nitrogen/generated/ # Generated Nitro code (do not edit)
├── docs/ # Documentation
└── examples/ # Example applications
# Fork the repository on GitHub first, then:
git clone https://github.com/YOUR_USERNAME/react-native-ble-nitro.git
cd react-native-ble-nitro
git remote add upstream https://github.com/zykeco/react-native-ble-nitro.git
npm installnpx nitro-codegenThis generates native bindings in nitrogen/generated/ from the TypeScript specs.
npm run buildnpm testnpm run devThis library is built on Nitro Modules, which provides:
- Direct JSI communication (no bridge)
- Type-safe native bindings
- High performance
- Automatic code generation
- Nitro Specs (
src/specs/): TypeScript interfaces that define the native API - Native Implementation: Platform-specific BLE implementations
- Generated Code (
nitrogen/generated/): Auto-generated native bindings
JavaScript/TypeScript → Compatibility Layer → Nitro Specs → Generated Native Code → Platform BLE APIs
-
Sync with upstream and create a feature branch:
git fetch upstream git checkout main git merge upstream/main git checkout -b feature/your-feature-name
-
Make your changes in the appropriate directories:
- TypeScript changes:
src/ - iOS changes:
ios/ - Android changes:
android/ - Tests:
src/__tests__/
- TypeScript changes:
-
Regenerate Nitro code if you modified specs:
npx nitro-codegen
-
Build and test:
npm run build npm test npm run lint -
Test with example app (if available):
cd example npm install npx expo run:ios # or run:android
- Never edit
nitrogen/generated/- these files are auto-generated - Always run
nitro-codegenafter modifying Nitro specs - Add tests for new functionality
# Run all tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch
# Run specific test file
npm test -- BleManager.test.ts- Unit Tests (
src/__tests__/): Test individual components - Integration Tests: Test component interactions
- Native Tests: Test platform-specific functionality
- Use Jest and @testing-library/react-native
- Mock Nitro modules appropriately
- Test both success and error scenarios
- Maintain high code coverage
- Test compatibility layer thoroughly
import { BleManager } from '../index';
import { State } from '../specs/types';
describe('BleManager', () => {
let manager: BleManager;
beforeEach(() => {
manager = new BleManager();
});
afterEach(async () => {
await manager.destroy();
});
it('should get Bluetooth state', async () => {
const state = await manager.state();
expect(typeof state).toBe('number');
expect(Object.values(State)).toContain(state);
});
});- Use strict mode TypeScript
- Prefer interfaces over types when possible
- Use explicit return types for public methods
- Follow naming conventions:
PascalCasefor classes, interfaces, enumscamelCasefor variables, functions, methodsUPPER_SNAKE_CASEfor constants
- Use Prettier for formatting
- Use ESLint for linting
- 2 spaces indentation
- Single quotes for strings
- Semicolons required
- Use JSDoc for public APIs
- Include @param and @returns tags
- Add @example for complex functions
- Keep comments up-to-date
/**
* Connects to a BLE device
*
* @param deviceId - The device identifier
* @param options - Connection options
* @returns Promise that resolves to the connected device
*
* @example
* ```typescript
* const device = await manager.connectToDevice('device-id', {
* autoConnect: true,
* timeout: 5000
* });
* ```
*/
async connectToDevice(
deviceId: string,
options?: ConnectionOptions
): Promise<Device> {
// Implementation
}- Fork the repository and create your branch from
main(see Development Setup above) - Make your changes following the guidelines above
- Add tests for new functionality
- Update documentation if needed
- Run the full test suite and ensure it passes
- Run linting and fix any issues
- Create a pull request with a clear description
## Description
Brief description of the changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Tests pass locally
- [ ] Added tests for new functionality
- [ ] Tested on iOS
- [ ] Tested on Android
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] No breaking changes (or documented)Use conventional commits:
type(scope): description
feat(ios): add MTU negotiation support
fix(android): resolve scanning permission issue
docs(readme): update installation instructions
test(manager): add connection timeout tests
Nitro specs define the interface between JavaScript and native code:
// src/specs/BleManager.nitro.ts
export interface BleManager extends HybridObject {
state(): Promise<State>;
startDeviceScan(
uuids: string[] | null,
options: ScanOptions | null,
listener: (error: NativeBleError | null, device: NativeDevice | null) => void
): Promise<void>;
}- Numeric enums only (no string enums)
- No index signatures (
{ [key: string]: value }) - No inline union types
- Structured data instead of flexible objects
- Specific callback signatures
Always run after spec changes:
npx nitro-codegenThis updates:
nitrogen/generated/ios/- Swift bindingsnitrogen/generated/android/- Kotlin bindingsnitrogen/generated/shared/- C++ core
- Implement protocols in
ios/ - Use Core Bluetooth framework
- Follow Swift conventions
- Handle background modes
- Manage permissions properly
class BleNitroBleManager: HybridBleManagerSpec {
private var centralManager: CBCentralManager!
public override init() {
super.init()
self.centralManager = CBCentralManager(delegate: self, queue: nil)
}
public func state() throws -> Promise<State> {
return Promise.resolve(mapCBManagerState(centralManager.state))
}
}- Implement interfaces in
android/src/main/kotlin/ - Use Android BLE APIs
- Handle permissions for all API levels
- Use coroutines for async operations
- Follow Android best practices
class BleNitroBleManager(private val context: ReactApplicationContext) : HybridBleManagerSpec {
private val bluetoothAdapter: BluetoothAdapter? by lazy {
(context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
}
override fun state(): Promise<State> = Promise.resolve {
when {
bluetoothAdapter == null -> State.Unsupported
!bluetoothAdapter.isEnabled -> State.PoweredOff
else -> State.PoweredOn
}
}
}- Nitro codegen fails: Check TypeScript specs for Nitro constraints
- Tests fail: Ensure mocks are properly configured
- Build errors: Run
npm run cleanand rebuild - Permission issues: Check Android/iOS permission configurations
- GitHub Issues: Report bugs or request features
- GitHub Discussions: Ask questions or discuss ideas
- Nitro Documentation: https://nitro.margelo.com/
- React Native BLE: Understanding the original API
By contributing, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to react-native-ble-nitro! 🚀