docs/ARCHITECTURE_V2.md
This branch starts a larger simplification effort focused on performance, reliability, and maintainability without rewriting the transport and protocol stack in one pass.
AppRuntime is now the composition root for startup, lifecycle, notification routing, and shared-content intake.BitchatApp is reduced to app wiring and no longer owns operational startup logic.AppEventStream introduces a typed async event surface for app-level events.PeerHandle and ConversationID establish canonical identity/conversation keys above the existing transport-specific IDs.IdentityResolver mirrors the current peer graph into canonical handles.ConversationStore now owns app-layer conversation context for the active public channel and selected private conversation, while also mirroring the current public/private message timelines under canonical IDs.AppRuntime now owns Tor, screenshot, and Nostr reconnect observer wiring instead of leaving that app-lifecycle graph inside ChatViewModel.PublicChatModel, PrivateInboxModel, PrivateConversationModel, ConversationUIModel, LocationChannelsModel, and PeerListModel provide focused view-facing models for public chat, private inbox state, selected-DM state, composer/message interactions, location-channel state, and peer counts. PrivateInboxModel now reads its message, unread, and selection state directly from ConversationStore instead of merging through PrivateChatManager.AppChromeModel now owns app-chrome presentation state for nickname editing, fingerprint routing, app-info/location sheet visibility, unread-private-chat affordances, and screenshot/privacy UI.ContentView, MessageListView, LocationChannelsSheet, LocationNotesView, CommandSuggestionsView, FingerprintView, and VerificationSheetView no longer talk directly to ChatViewModel or location singletons for their primary app-layer state; they consume feature-scoped models injected from AppRuntime.PeerListModel now consumes the runtime-owned LocationChannelsModel instead of reading the location manager directly, and QR scan parsing is funneled through VerificationModel rather than the SwiftUI views.ChatViewModel is thinner on the app side: startup wiring now lives in ChatViewModelBootstrapper, send/lifecycle/transport seams live in focused coordinators, and peer-list notification/timer state now lives in ChatPeerListCoordinator instead of the main view model.ChatViewModel remains the active domain/UI bridge so the app can keep working while we migrate feature-by-feature.ConversationStore now owns the selected conversation context and private-inbox read state, which lets feature models read one runtime-managed source of truth for focus/navigation and DM timelines even while legacy mutation paths still flow through ChatViewModel.ChatViewModel without forcing a big rewrite of the domain/message paths.ContentView, which keeps the view from re-implementing peer lookup rules.ChatViewModel.BitchatApp now injects only feature-scoped models into the view tree, which removes the old pattern of keeping ChatViewModel as a global environment object for the entire app shell.ChatViewModel coordinators are intentionally transitional: they shrink the main file and isolate composition, lifecycle, transport, and peer-list responsibilities without forcing a risky rewrite of the transport/BLE core in the same pass.ChatViewModel to focused feature models backed by ConversationStore.ChatViewModel.The Bluetooth architecture branch begins step 2 by adding a typed TransportEvent boundary while preserving the legacy BitchatDelegate bridge. New transport code should emit typed events first, with delegate forwarding used only as a compatibility adapter during migration.
The branch also starts carving performance-sensitive BLE scheduling state out of BLEService: pending write backpressure now lives in BLEOutboundWriteBuffer, giving the outbound hot path a focused, unit-tested component before deeper fragmentation and link-scheduler work.
The next transport slice continues that path by extracting ingress link memory and outbound fanout selection. BLEIngressLinkRegistry now owns duplicate/last-hop tracking, ingress peer memory, and direct-link sender binding decisions, while BLEFanoutSelector owns deterministic broadcast subsetting and ingress-peer/link exclusion. BLEService still coordinates CoreBluetooth callbacks, but these hot-path decisions are now pure, covered units instead of inline dictionary logic.