React Native iOS-first app for Smart Knee BLE pairing, workout sessions, and sensor-driven insights.
Screenshots · Getting Started · Architecture · Project Structure
CGC 2026 Client is an Expo Router mobile app focused on connecting to a Smart Knee device over BLE, collecting session data, and integrating with backend workout APIs. The app combines authenticated API workflows, real-time BLE streaming, and context-driven state composition.
It is currently implemented with an iOS-first BLE architecture (react-native-ble-plx) while still keeping Expo/React Native cross-platform structure.
- BLE device management - Scan, pair, reconnect, and recover Smart Knee connections with iOS-specific handling.
- Workout and session flows - Load workout types/session history and run active workout screens with API-backed data.
- Sensor streaming pipeline - Stream sample data from BLE characteristics into app-level providers and services.
- Auth + secure token handling - Clerk authentication with secure token storage.
- CSV export tooling - Export captured sensor data through iOS export provider flows.
| Layer | Technology |
|---|---|
| Framework | React Native 0.79 + Expo SDK 53 |
| Navigation | Expo Router (file-based routing) |
| State management | React Context + XState + React Query |
| BLE | react-native-ble-plx |
| API Client | Axios-based authenticated client |
| Auth | Clerk (@clerk/clerk-expo) + Expo Secure Store |
| Styling/UI | React Native StyleSheet + themed components |
| Language | TypeScript |
- Node.js 18+
- npm
- Xcode (for iOS Simulator or physical device builds)
- Optional: Android Studio (if you want Android simulator support)
npm installCreate a .env file in the project root using .env.example.
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=
EXPO_PUBLIC_API_BASE_URL=# Start Expo dev server
npx expo startYou can also run native iOS builds directly:
npx expo run:ios --device
npx expo run:ios --device --configuration Release
npx expo run:ios --device --configuration Release --no-bundler
npx expo prebuild --clean && npx expo run:ios
Package scripts:
npm run start
npm run ios
npm run android
npm run webnpm run lintnpm run reset-projectThis keeps the Expo starter reset behavior in case you still need it for experimentation.
client/
├── app/ # Expo Router screens/routes
│ ├── (tabs)/ # Main tabbed navigation (home/activities/dev)
│ ├── workout/ # Workout detail routes
│ ├── session/ # Session detail routes
│ ├── bluetooth.tsx # BLE device discovery/pair UI
│ └── _layout.tsx # Root provider composition + stack routes
├── contexts/ # App-level providers (BLE, auth, storage, workout, export)
├── services/ # Domain services (knee device, battery, workout APIs)
├── hooks/ # Reusable hooks (queries, theme, route guards)
├── components/ # UI and feature components
├── constants/ # BLE UUIDs, colors, environment config
├── lib/ # Helper utilities (BLE connection helpers, auth client, math)
└── types/ # Shared TypeScript types
The app composes feature providers in app/_layout.tsx to create a clear top-down data and capability flow:
QueryProviderfor server state caching and async request orchestration.StorageProviderfor persisted app/device keys.AuthProviderandAuthApiProviderfor Clerk auth + authenticated API client.WorkoutAPIProviderfor workout/session backend interactions.BLEProviderdelegating toIOSBleProvideron iOS.KneeDeviceProvider,CalibrationProvider,BatteryProvider, andWorkoutProviderfor hardware/session domain logic.
BLE state transitions are managed with XState in the iOS provider (idle -> scanning -> pairing -> connected) and wrapped with a type-safe context API for UI usage.
┌─────────────────────────────────────────────────────────────┐
│ UI Layer (Expo Router screens + feature components) │
├─────────────────────────────────────────────────────────────┤
│ Context Layer (Auth, BLE, KneeDevice, Workout, Battery) │
├─────────────────────────────────────────────────────────────┤
│ Services Layer (BLE service, workout API service, exports) │
├─────────────────────────────────────────────────────────────┤
│ Platform + External (iOS BLE stack, Clerk, backend API) │
└─────────────────────────────────────────────────────────────┘