Imported from gencau/test-practices-agent-configurations (
dataset/repos/bcgov§bc-wallet-mobile/AGENTS.md). Install upstream withnpx skills add gencau/test-practices-agent-configurations --skill bcgov§bc-wallet-mobile. Copyright stays with the author.
Project Context & AI Persona
You are an expert mobile developer specializing in React Native, clean architecture, performance optimization, and robust UI implementation. You prioritize maintainability, strict adherence to established patterns, and clear communication.
Architecture Patterns
MVVM (Model-View-ViewModel)
This project follows a React-adapted MVVM pattern using hooks. The traditional class-based ViewModel is replaced with custom hooks that encapsulate state and logic.
ViewModel Hook (useXxxViewModel)
- Custom React hook that serves as the ViewModel layer in MVVM
- Consumes the Model layer (stores, API hooks, services) and exposes state/actions to the View
- Returns state values and action handlers for the View to consume
- Should not contain any TSX or UI components
Note: The Model layer is composed of
useStore, API hooks (such asuseApi), and services. ViewModel hooks consume and orchestrate these.
// useSetupStepsViewModel.tsx
const useSetupStepsViewModel = (navigation: StackNavigationProp<...>) => {
const { t } = useTranslation()
const [store] = useStore<BCState>()
const [isCheckingStatus, setIsCheckingStatus] = useState(false)
// Derived state
const steps = useSetupSteps(store)
// Action handlers
const handleCheckStatus = useCallback(async () => {
setIsCheckingStatus(true)
try {
// Business logic here
navigation.navigate(BCSCScreens.VerificationSuccess)
} finally {
setIsCheckingStatus(false)
}
}, [navigation])
const stepActions = useMemo(() => ({
nickname: () => navigation.navigate(BCSCScreens.NicknameAccount),
id: () => navigation.navigate(BCSCScreens.IdentitySelection),
}), [navigation])
return {
steps,
stepActions,
isCheckingStatus,
handleCheckStatus,
}
}
export default useSetupStepsViewModel
View (Screen/Component)
- React component that consumes the ViewModel hook
- Handles UI rendering and user interactions
- Should contain minimal logic—delegate to the ViewModel hook
- Focus on layout, styling, and presenting data
// SetupStepsScreen.tsx
const SetupStepsScreen: React.FC<SetupStepsScreenProps> = ({ navigation }) => {
const { t } = useTranslation()
const { Spacing, ColorPalette } = useTheme()
// Consume the ViewModel hook
const { steps, stepActions, isCheckingStatus, handleCheckStatus } =
useSetupStepsViewModel(navigation)
return (
<ScreenWrapper>
<SetupStep
title={t('BCSC.Steps.Nickname')}
completed={steps.nickname.completed}
onPress={stepActions.nickname}
/>
<Button
title={t('BCSC.Steps.CheckStatus')}
onPress={handleCheckStatus}
loading={isCheckingStatus}
/>
</ScreenWrapper>
)
}
Pattern Benefits
- Separation of concerns: Logic in hooks, rendering in components
- Testability: ViewModel hooks can be tested independently with
renderHook - Reusability: ViewModel hooks can be shared across multiple views if needed
- React-native: Leverages React's built-in reactivity (
useState,useMemo,useCallback)
Directory Structure
This codebase uses a feature-based structure where each feature contains its own screens, components, ViewModels, and models. This promotes cohesion within features while maintaining separation of concerns.
/app/src
/bcsc-theme # BC Services Card app theme
/api # API clients and services
/components # Shared UI components across features
/contexts # React contexts
/features # Feature modules
/auth # Authentication feature
/home # Home screen feature
Home.tsx # Screen component
/components # Feature-specific components
/verify # Identity verification feature
VerificationMethodSelectionScreen.tsx
SetupStepsScreen.tsx
useVerificationMethodViewModel.tsx
useSetupStepsViewModel.tsx
/components # Feature-specific components
/send-video # Sub-feature
/live-call # Sub-feature
/pairing # Device pairing feature
/settings # Settings feature
/hooks # Shared hooks
/navigators # Navigation configuration
/types # TypeScript types
/utils # Utility functions
/bcwallet-theme # BC Wallet app theme (similar structure)
/components # App-wide shared components
/constants.ts # App constants
/localization # i18n translations
/services # Shared services
/store # State management
/utils # Shared utilities
Key conventions:
- Tests are co-located with their source files (e.g.,
Screen.tsx+Screen.test.tsx) - Feature-specific components stay within the feature folder
- Shared components are elevated to
/componentsat the appropriate level
Guidelines
-
Separation of Concerns
- ViewModel hooks should not contain JSX or UI components
- Views should delegate logic to ViewModel hooks
- Keep styling and layout in Views, business logic in hooks
-
Data Flow
- ViewModel hook manages state and exposes it to the View
- User actions call handlers returned by the ViewModel hook
- Use
useMemofor derived state,useCallbackfor stable handlers
-
Testing
- ViewModel hooks: Test with
renderHookfrom@testing-library/react-native - Views: Test UI interactions and rendering with mocked hooks
- Co-locate tests with source files (e.g.,
useSetupStepsViewModel.test.ts)
- ViewModel hooks: Test with
-
State Management
- ViewModel hook owns the state for its View
- Use React hooks (
useState,useMemo,useCallback) for reactivity - Access global state via
useStoreor context hooks
-
Naming Conventions
- ViewModel hooks:
use[Feature]ViewModel(e.g.,useServiceOutageViewModel,useTransferQRScannerViewModel). Some older hooks use aModelsuffix (e.g.,useSetupStepsModel); these may be renamed toViewModelover time for consistency. - Views:
[Feature]Screenor descriptive component names
- ViewModel hooks:
-
Error Handling
- User-facing errors belong in the UI layer (Views or ViewModel hooks), not in API/data hooks. API hooks should throw errors and let callers decide whether and how to surface them.
- Use
emitErrorAlertwithAppError.fromErrorDefinition(ErrorRegistry.XXX, { cause: error })to show errors as native alerts. Prefer this overemitErrorwith registry keys. - Callers should inspect error types (e.g.,
isBcscNativeError) and choose the appropriate response — some errors are critical (onboarding, auth), others are intentionally non-critical (background tasks, optional nickname updates). - API hooks should remain single-responsibility: make the API call, return data, throw on failure. No UI side effects.
Commit Message and PR Title Formatting
When suggesting commit messages or pull request titles, always follow the Conventional Commits format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types
feat: A new featurefix: A bug fixdocs: Documentation only changesstyle: Changes that do not affect the meaning of the code (white-space, formatting, etc)refactor: A code change that neither fixes a bug nor adds a featureperf: A code change that improves performancetest: Adding missing tests or correcting existing testsbuild: Changes that affect the build system or external dependenciesci: Changes to CI configuration files and scriptschore: Other changes that don't modify src or test filesrevert: Reverts a previous commit
Scope
The scope should be the name of the architectural layer or component affected:
model: Changes to domain modelsviewmodel: Changes to ViewModelsview: Changes to Views/UIadapter: Changes to data adaptersservice: Changes to external services- Specific feature names:
auth,wallet,credentials, etc.
Examples
feat(viewmodel): add user profile editing capabilityfix(adapter): correct date transformation in UserAdapterrefactor(model): simplify user repository interfacetest(viewmodel): add unit tests for authentication flowdocs(architecture): update MVVM pattern documentationstyle(view): adjust spacing in credential card component
Pull Request Titles
Pull request titles should follow the same conventional commit format to maintain consistency between commits and PRs.
General Guidance
Commit Messages
- Keep descriptions concise and under 72 characters when possible
- Use the imperative mood ("add" not "added" or "adds")
- Do not capitalize the first letter of the description
- No period at the end of the description
- Use the body to explain what and why vs. how
- Use conventional commits for clarity
Code Quality
- Always maintain clear separation between layers
- Use adapters when transforming data between layers
- Write unit tests for each layer independently
- Keep ViewModels framework-agnostic (no UI dependencies)
- Document complex business logic in Models
- Keep Views thin—move logic to ViewModels
- Keep tests close to the code they are testing
- Follow established naming conventions for clarity
Variant Configuration Files (variant.env)
Quoting Rules
- Prefer single quotes (
'...') for all values by default. Single quotes denote literal strings and prevent unintended shell expansion (e.g.,$(...)is preserved as-is). - Use double quotes (
"...") only when shell variable substitution or interpolation is explicitly required. - When in doubt, use single quotes.
Examples
# Correct — literal values use single quotes
APP_NAME='BC Services Card'
IOS_BUNDLE_ID='ca.bc.gov.iddev.servicescard'
IOS_PRODUCT_NAME='$(TARGET_NAME)'
# Incorrect — double quotes risk shell expansion of $(...) and similar syntax
IOS_PRODUCT_NAME="$(TARGET_NAME)"
Rationale
These files are sourced in shell contexts (e.g., GitHub Actions source variant.env). Double-quoted strings containing $, backticks, or ! will be interpreted by the shell, leading to unexpected behaviour. Single quotes ensure values are loaded exactly as written.