Instruction file imported from Vonage/vonage-video-ios-app (
.github/instructions/ui.instructions.md). Copyright stays with the author.
UI rules
Architecture
- Views use MVVM:
ObservableObjectViewModels with@Publishedproperties, always annotated with@MainActor. - Two-tier structure:
*Screen(coordinator — observes ViewModel, maps state to actions) wraps a*View(stateless presentation). Keep this separation when adding new screens. - Navigation is decoupled via the
ActionHandlertypealias ((Action) -> Void) — views emitActionenum cases, never perform navigation directly.
Factory / Wireframe pattern
- Each flow has a
*Factoryclass (in aWireframe/folder) that constructs the ViewModel and View together. - Factories receive all dependencies through their initialiser from
DependencyContainer. ViewModels never instantiate their own dependencies. - Some factories return
(view, viewModel)tuples so the caller can retain the ViewModel for external control. - Mark factory
make()methods@MainActor.
Reusable components (VERACommonUI)
- Prefer existing shared components:
FilledButton,OutlinedButton,ControlButton,CircularControlButton,CardView,AvatarInitials,AvatarGroup,ToastView,DismissibleOverlayModifier. - New shared components go in
VERACommonUI/UI/. Use@ViewBuildergeneric containers (seeCardView<Content: View>pattern). - Create
ViewModifiertypes for reusable visual behaviors (seeAdaptiveFontModifier,HorizontalFlipModifier,Pulsating).
Design tokens
- Reference semantic colors via
VERACommonUIAsset.SemanticColors.*(e.g.,.primary,.error,.surface,.textSecondary). These are generated fromTheme/semantics.json— do not hardcode hex values. - Use
.adaptiveFont(_:)modifier withTypographyStylecases (.headline,.bodyBase,.caption, etc.) for responsive typography. This modifier adapts to horizontal/vertical size class. - Use
BorderRadiusenum (.none,.extraSmall,.small,.medium,.large,.extraLarge) via the.cornerRadius(_:)extension.
Domain separation
UIParticipantwraps the domainParticipantwith UI-only state (isPinned,canBePinned,onTogglePin). Never add UI concerns to domainParticipant.- State derivation (e.g.,
MeetingRoomState) usesPublishers.CombineLatestto merge domain streams with UI preferences. Keep domain publishers and UI-only state in separate Combine chains. - Do not import
VERAVonageor any SDK module fromVERACommonUIorVERACore.
Localization
- Use the
String.localized()extension orString(localized:)for user-facing text. - String catalogs (
.xcstrings) are located in each module'sResources/. Supported languages: English (en), Spanish (es). - For pluralization use
String.pluralizeIfNeeded(count:).
Previews
- Provide
#Previewblocks for new components. UsePreviewDatahelpers for mock data. - Include dark mode previews with
.preferredColorScheme(.dark)for components that use semantic colors.
Platform considerations
- Guard iOS 26+ glass morphism effects behind
if #availablewith opaque material fallbacks. VERACommonUIandVERACoretarget both iOS 16+ and macOS 14.6+ — avoid UIKit-only APIs.