Page - Currency Lens

Screen: AppRoute.currencyLens File: Utiliship/Features/CurrencyLens/CurrencyLensView.swift ViewModel: Utiliship/Features/CurrencyLens/CurrencyLensViewModel.swift Use Case: Utiliship/Features/CurrencyLens/CurrencyLensUseCase.swift

Purpose

Live AR-style currency overlay. The camera feed runs continuously; the use case scans video frames for price text (OCR), converts each detected amount using cached exchange rates, and renders TrackedPrice overlays pinned to their screen position. Users can lock a single overlay, adjust zoom, and toggle flash.

Component Tree

CurrencyLensView
├── ZStack
│   ├── CameraPreviewView(session: state.captureSession)  — full-screen camera
│   │   └── .gesture(MagnificationGesture) → viewModel.setZoom(_:)
│   ├── ForEach state.trackedPrices → PriceOverlayView   — AR overlays
│   └── controlBar (bottom)
│       ├── currencyPickerButton  — shows source/target picker sheet
│       ├── flashButton           — viewModel.toggleFlash()
│       └── lockButton            — viewModel.lockPrice(_:)
├── .withBannerAd(placement: .currencyLens)
└── .sheet(isPresented: $showCurrencyPicker) → currencyPickerSheet

ViewModel State

FieldTypeDescription
cameraAuthStatusCameraAuthorizationStatus.notDetermined, .authorized, .denied
isCameraActiveBoolTrue when AVCaptureSession is running
captureSessionAVCaptureSession?Injected into CameraPreviewView
sourceCurrencyCurrencyCurrency of detected prices (default .usd)
targetCurrencyCurrency?Conversion target; nil falls back to base currency
autoDetectCurrencyBoolAuto-detect currency symbol from OCR text
trackedPrices[TrackedPrice]Active AR overlays with smoothed bounding boxes
lockedPriceTrackedPrice?User-pinned overlay; pauses detection for that price
isFlashOnBoolTorch state
isFlashAvailableBoolDevice capability
zoomLevelCGFloatCurrent zoom (clamped minZoom…maxZoom)
minZoom / maxZoomCGFloatDevice-reported zoom range
errorString?Camera permission or session error
isProcessingBoolTrue while a frame is being analysed

Data Flow