The project demonstrates Native integration back and forth between iOS and React Native
RNPriceListViewControllerpresents React Native module nameCDC_Interview- Native API calling via Turbo Module in
RNNativeInterviewModule
Since this project doesn't point to any remote repo so I'll leave the commit messages here to demonstrate my workflow for the solution:
- chore: clean placeholder txt extensions, initialize project & install packages
- feat(native): create RNNativeInterviewModule swift file and link to .mm file
- feat(native): implement fetchPriceList using USDPriceUseCase & AllPriceUseCase JSON loading logic
- feat(native): implement RNPriceListViewController hosting React native root view
- feat(js): observe change from
Support EURand refetch corresponding data - feat(js): implement useFetchPriceList hook with normalization
- feat(js): optimize Crypto list performance with best practices
- ui: add PriceList component with loading/error/empty states
- ...
- Implemented
Support EURfeature flag wiring between native iOS (FeatureFlagProviderusing RxSwift BehaviorRelay) and React Native throughRNNativeInterviewEventsEmitterModule. - When flag OFF: native use case
USDPriceUseCasereturns JSON subset (USD only) -> JS normalizes to unifiedCryptoCurrencymodel. - When flag ON: native
AllPriceUseCasereturns extended list including EUR; JS appends EUR price after USD when available. - Event: Native emits
onFeatureFlagDataChanged-> JS hook (useFetchPriceListviaInterviewEventsEmitter) triggers refetch & state reconciliation.
- NativeInterviewModule defined in
specs/NativeInterviewModule.tsis a Turbo Module implementation. - Promise method
fetchPriceList()loads appropriate bundled JSON (usdPrices.jsonorallPrices.json) depending on feature flag state at invocation time. - Data normalization on both native and JS sides handle return the Object with fields we needed.
RNPriceListViewControllerhosts the React component inside a native navigation stack usingRCTReactNativeFactory(Fabric enabled). This ensures a single RN runtime & renderer instance.AppDelegatesetsmoduleNameand custom dependency provider wiring without eagerly instantiating multiple bridges.
- Displays list of crypto entries with conditional EUR suffix.
- Added tag chips (e.g. deposit / withdrawal) rendered inline with name for better scannability.
- Loading, error, empty states implemented for robust UX.
- Pull to refresh ties to native fetch via the module.
- FlatList optimizations: constant item height (70),
getItemLayout,removeClippedSubviews, tunedinitialNumToRender,windowSize,maxToRenderPerBatch, memoized row component (PriceRow) + stable callbacks (useCallback). - Avoids unnecessary renders: extracted separator & empty component; removed transient state copies.
- Feature flag listener uses a single subscription and cleans up properly to avoid memory leaks.
| Concern | Action | Benefit |
|---|---|---|
| List re-renders | memo(PriceRow), stable callbacks |
Reduced JS work per state change |
| Layout measurement cost | getItemLayout with fixed item + separator height |
Faster scroll start & less layout thrash |
| Memory / mounting pressure | Windowing props (initialNumToRender, windowSize, batching) |
Lower peak memory & smoother scroll |
| Bridge overhead | Single RN runtime via factory | Fewer native/JS context creations |
| Conditional EUR formatting | Inline numeric formatting with early exits | Minimal overhead |
- None
- Integrate TypeScript codegen types directly from schema to ensure strict alignment.
- Introduce unit tests (Jest) for hook normalization & price formatting; snapshot tests for list row.
- Add E2E tests (Detox) toggling feature flag to validate live update.
- Migrate formatting to native (Swift NumberFormatter) with locale awareness & pass formatted strings.
- Implement search / filtering with debounced input +
useTransition(if RN concurrent features used).
- Pre-size underlying arrays & diff via stable keys to later adopt
FlashList(if allowed) for large datasets. - Offload heavy parsing to background thread in native (if JSON size grows) and pass typed objects via JSI.
- Clean separation between native data access, flag propagation, and JS presentation.
- Single responsibility modules: hook vs UI list vs native use cases.
- Clear fallback & document as code comments to accelerate debugging during interview review.
- Unzip project and install dependencies:
yarn install cd ios/CDC_Interview && pod install
- Run the project:
yarn ios
- Launch app; observe USD prices only initially (flag off).
- Navigate to Settings -> toggle
Support EUR. - Return (or observe live) list now shows
USD: X EUR: Ywhere available. - Pull to refresh to confirm manual refetch path.
Sample Normalized Item Shape
interface CryptoCurrency {
id: number;
name: string;
usd: number; // always present
eur?: number; // present only if flag ON and value exists
tags?: string[]; // optional meta
}Reduce API response time simulation to 1s
DispatchQueue.global()
.asyncAfter(deadline: .now() + 1)
