Codebase Guide
Top-Level Structure
Meshtastic/
├── MeshtasticApp.swift # @main App struct
├── MeshtasticAppDelegate.swift # UIApplicationDelegate (SiriKit)
├── AppState.swift # @EnvironmentObject root state
├── Accessory/ # BLE/TCP/serial connectivity
├── API/ # REST API helpers
├── AppIntents/ # Siri / Shortcuts intents
├── CarPlay/ # CarPlay scene
├── Enums/ # Shared enumerations
├── Extensions/ # Swift extensions (Logger, Date, String…)
├── Helpers/ # Utility types (no UI)
├── Intents/ # INIntent handlers
├── Measurement/ # Unit/measurement formatting
├── Model/ # @Model SwiftData types
├── Persistence/ # PersistenceController, MeshPackets actor
├── Resources/ # Assets, docs bundle, Info.plist
├── Router/ # Router + NavigationState
├── Tips/ # TipKit tips
└── Views/ # SwiftUI views
├── Bluetooth/ # BLE connect view
├── Map/ # Map + overlay views
├── Messages/ # Channel + DM views
├── Nodes/ # Node list + detail
└── Settings/ # All settings views
MeshtasticProtobufs/ # Swift Package wrapping generated protobufs
MeshtasticTests/ # Test target (Swift Testing)
scripts/ # Build and utility scripts
specs/ # Feature specs (speckit workflow)
Project Generation
Meshtastic.xcodeproj is generated from project.yml by XcodeGen rather than hand-maintained. project.yml is the source of truth for targets, build settings, and schemes; the generated .xcodeproj is still committed so the repo builds without installing XcodeGen first.
Most source directories — including Meshtastic/Views/, Meshtastic/Model/, Meshtastic/Accessory/, MeshtasticTests/, Shared/, and Widgets/ — are configured as type: syncedFolder (Xcode 16 synchronized groups). A new file added under one of these directories is picked up automatically; there is no membership checkbox or pbxproj entry to add.
If you change project.yml (or .xcodegen-version), regenerate the project and commit the result:
xcodegen generate
The xcodegen-drift.yml CI check regenerates the project on every PR that touches project.yml, .xcodegen-version, Meshtastic.xcodeproj/**, or any .swift file, and fails if the committed project doesn't exactly match — this is what keeps the generated project honest as the source of truth stays in project.yml. XcodeGen's output is version-sensitive, so .xcodegen-version pins an exact release; install that exact version (not whatever Homebrew currently carries) before regenerating locally.
Keep sibling group names unique. XcodeGen 2.46.0 does not produce a stable order when two root groups have the same display name, which makes the drift check intermittent. Shared tvOS sources used by the visionOS target therefore live under the distinct virtual group Meshtastic TV (Vision shared) rather than a second root group named Meshtastic TV.
Key Files
| File | Purpose |
|---|---|
project.yml | XcodeGen project specification — the source of truth for Meshtastic.xcodeproj |
Router/Router.swift | Central navigation controller (@MainActor) |
Router/NavigationState.swift | Per-tab navigation state enums |
Extensions/Logger.swift | Typed OSLog loggers for all subsystems |
Persistence/PersistenceController.swift | SwiftData ModelContainer setup |
Model/MeshtasticSchema.swift | VersionedSchema + SchemaMigrationPlan |
Accessory/Accessory Manager/AccessoryManager.swift | BLEBLE (Bluetooth Low Energy). The default wireless connection between a node and a phone client./TCPTCP (Transmission Control Protocol). One of the three ways a client reaches a node, alongside serial and Bluetooth. Clients connect over TCP on port 4403. manager root class |
Extension File Pattern
Large manager classes are split into +Extension files grouped by concern:
// AccessoryManager.swift — properties and init only
// AccessoryManager+Connect.swift — connection lifecycle
// AccessoryManager+ToRadio.swift — outbound packet methods
// AccessoryManager+FromRadio.swift — inbound packet handling
Follow the same pattern when adding new subsystems to AccessoryManager or other large classes.
Logging
All logging uses typed Logger instances from Meshtastic/Extensions/Logger.swift. Never use print().
Logger.mesh.debug("Packet received: \(packet.id)")
Logger.transport.error("BLE write failed: \(error)")
Available categories: .admin, .data, .docs, .mesh, .mqtt, .radio, .services, .statistics, .transport, .tak
View Hierarchy
Views are in Meshtastic/Views/. Each major feature has its own subdirectory. The root ContentView hosts a TabView keyed on NavigationState.
Views that need connectivity inject @EnvironmentObject var bleManager: BLEManager (legacy name; newer code uses AccessoryManager). Views that need navigation inject @EnvironmentObject var router: Router.
Localization
All user-visible strings must use String(localized:) or LocalizedStringKey. The source strings file is Localizable.xcstrings in the project root.