Skip to content

feat: Implement iOS support and unify Compose Multiplatform infrastructure#4876

Merged
jamesarich merged 19 commits intomainfrom
feat/ios-gates
Mar 21, 2026
Merged

feat: Implement iOS support and unify Compose Multiplatform infrastructure#4876
jamesarich merged 19 commits intomainfrom
feat/ios-gates

Conversation

@jamesarich
Copy link
Copy Markdown
Collaborator

@jamesarich jamesarich commented Mar 21, 2026

This pull request expands Kotlin Multiplatform (KMP) support across the codebase, improves documentation for cross-platform development, and updates the build and CI process to ensure iOS compatibility. It also updates the BLE stack to use Kable, enhances shared testing and UI guidelines, and cleans up static analysis configuration.

Key changes include:

Kotlin Multiplatform (KMP) Expansion and Build Process:

  • Added iosArm64() and iosSimulatorArm64() targets to the standard KMP configuration in build-logic/convention, ensuring all relevant modules compile for iOS as well as JVM.
  • Updated the CI workflow in .github/workflows/reusable-check.yml to include iOS Simulator Arm64 smoke compile steps for all KMP modules, not just JVM.

Documentation Updates for Cross-Platform Development:

  • Updated architecture and module documentation in .github/copilot-instructions.md, AGENTS.md, and GEMINI.md to reflect new KMP targets, BLE stack migration, feature module targets, and Compose Multiplatform best practices. This includes stricter guidance on string resources, QR code generation, and test libraries. [1] [2] [3] [4] [5]

Bluetooth Low Energy (BLE) Stack Migration:

  • Migrated the BLE stack from Nordic libraries to Kable across documentation and module references, reflecting a shift to a more multiplatform BLE solution. [1] [2] [3]

Navigation and UI Improvements:

  • Updated the MainScreen composable in app/src/main/kotlin/org/meshtastic/app/ui/Main.kt to use MeshtasticNavSavedStateConfig for navigation back stack state, preparing for enhanced multiplatform navigation support. [1] [2]

Static Analysis and Build Environment:

  • Cleaned up the Detekt baseline by removing obsolete suppressed issues, reflecting codebase improvements and refactors.
  • Updated the JDK reference in .jdk to a specific JDK path, standardizing the build environment.

These changes collectively improve multiplatform support, developer experience, and code quality across the project.

…cture

* Migrate platform-specific string formatting and `Dispatchers.IO` to common utilities (`formatString` and `ioDispatcher`).
* Refactor Compose UI previews to use custom multiplatform-compatible annotations.
* Add iOS build targets (`iosArm64`, `iosSimulatorArm64`) across core and feature modules.
* Implement iOS-specific persistence using Okio and Room.
* Add platform no-op stubs and CI compilation validation for iOS targets.
* Update documentation roadmap to reflect iOS and JetBrains Compose migration progress.
…e Multiplatform

* Centralized polymorphic NavKey serialization in NavigationConfig for Navigation 3 saved state.
* Relocated desktop-specific screens to feature module jvmMain source sets to thin out the desktop module.
* Migrated Message, Contacts, and MessageListPaged screens into commonMain for full cross-platform UI reuse.
* Replaced PredictiveBackHandler with Compose Multiplatform NavigationBackHandler.
* Replaced legacy tooling preview wildcards with explicit, multiplatform-compatible imports.
…ultiplatform UI

* Reflected that desktop completely relies on feature modules via commonMain exported graphs.
* Removed references to desktop-specific configuration screens which are now fully shared.
* Migrated NodeListScreen entirely to commonMain via platform-agnostic toast resources.
* Moved AdaptiveNodeListScreen to commonMain to deduplicate JetBrains adaptive pane scaffolds across JVM and Android.
* Standardized right-pane NodeDetailScreen via an expect/actual definition to isolate complex Android-specific location and compass permissions away from the common implementation.
* Removed the duplicate DesktopAdaptiveNodeListScreen implementation in the desktop module.
* Removed Android-specific dependencies (LocalContext, toUri) in favor of shared multiplatform equivalents (CommonUri.parse, rememberShowToastResource).
* Created a generic callback for NFC settings enabling feature modules to be platform agnostic.
* Deleted the duplicate DesktopNetworkConfigScreen implementation entirely.
…igScreen

* Added `@Suppress` to the NetworkConfigScreen function since it was moved from androidMain to commonMain, causing it to fail strict detekt length thresholds.
* Cleaned up obsolete baseline signatures and resolved legacy entries from previously extracted KMP views and viewmodels.
@jamesarich jamesarich requested a review from Copilot March 21, 2026 20:38
@github-actions github-actions bot added the enhancement New feature or request label Mar 21, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables compile-only iOS target support across the KMP modules and further unifies the Compose Multiplatform + Navigation 3 infrastructure so Android/Desktop share more common code paths.

Changes:

  • Add iOS targets (iosArm64, iosSimulatorArm64) and CI smoke compilation for iOS Simulator Arm64.
  • Introduce multiplatform utility abstractions/stubs (e.g., ioDispatcher, formatString, URI/toast/NFC helpers) to keep commonMain compiling on iOS.
  • Refactor feature navigation/screens to use expect/actual entry points and shared graphs, reducing desktop-specific wiring.

Reviewed changes

Copilot reviewed 147 out of 149 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
gradle/libs.versions.toml Adds NavigationEvent + Compose tooling preview dependencies in version catalog
fix_dispatchers.py Adds a helper script to replace Dispatchers.IO usages with ioDispatcher
feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt Desktop actual implementations for Settings entry screens
feature/settings/src/iosMain/kotlin/org/meshtastic/feature/settings/debugging/NoopStubs.kt iOS no-op stub for log export
feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/PacketResponseStateDialog.kt Uses formatString for KMP-safe formatting
feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/LoadingOverlay.kt Uses formatString for KMP-safe formatting
feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt Switches to ioDispatcher for background work
feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt Removes Android-only URI/NFC helpers; introduces CommonUri & callback injection
feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt Moves shared entry wiring toward expect/actual screens
feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt Android actual implementations for Settings entry + config screens
feature/settings/detekt-baseline.xml Updates baseline (removes some suppressed issues)
feature/node/src/jvmMain/kotlin/org/meshtastic/feature/node/navigation/TracerouteMapScreens.kt Desktop placeholder actual for traceroute map
feature/node/src/jvmMain/kotlin/org/meshtastic/feature/node/metrics/PositionLogScreens.kt Desktop placeholder actual for position log
feature/node/src/jvmMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt Desktop actual for node detail screen using shared content
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PositionLogScreens.kt Introduces expect for position log screen
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PositionLogComponents.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt Uses formatString during export formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt Switches to ioDispatcher
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt Adds expect for NodeDetailScreen
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/CommonNodeRequestActions.kt Switches to ioDispatcher
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt Uses formatString for KMP-safe formatting
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/CompassBottomSheet.kt Replaces Math.toRadians with PI math for KMP-friendliness; uses formatString
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/compass/CompassViewModel.kt Uses formatString
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/navigation/TracerouteMapScreens.kt Android actual traceroute map screen wrapper for MetricsViewModel
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/navigation/NodesNavigation.kt Adds expect traceroute map API + default args for desktop assembly
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/navigation/AdaptiveNodeListScreen.kt Migrates back handling to NavigationEvent-based handler and adds injected navigation callback
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/metrics/PositionLog.kt Marks PositionLogScreen as actual
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt Replaces Android context toast with multiplatform toast helper
feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreen.kt Marks NodeDetailScreen as actual and aligns signature with expect
feature/messaging/src/jvmMain/kotlin/org/meshtastic/feature/messaging/navigation/ContactsEntryContent.kt Desktop actual contacts entry wiring with custom detail pane
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/ui/contact/ContactsViewModel.kt Switches to ioDispatcher
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/QuickChatViewModel.kt Switches to ioDispatcher
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/QuickChat.kt Uses encodeToByteArray() for KMP-friendly byte sizing
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt Switches to ioDispatcher
feature/messaging/src/androidTest/kotlin/org/meshtastic/feature/messaging/component/MessageItemTest.kt Updates preview parameter provider import
feature/messaging/src/androidMain/kotlin/org/meshtastic/feature/messaging/ui/contact/Contacts.kt Removes Android-only URI conversion, uses MeshtasticUri; swaps toast impl
feature/messaging/src/androidMain/kotlin/org/meshtastic/feature/messaging/ui/contact/AdaptiveContactsScreen.kt Migrates predictive back handling to NavigationEvent; allows custom detail pane
feature/messaging/src/androidMain/kotlin/org/meshtastic/feature/messaging/navigation/ContactsNavigation.kt Moves Contacts entry content to expect/actual; provides default scroll events
feature/messaging/src/androidMain/kotlin/org/meshtastic/feature/messaging/navigation/ContactsEntryContent.kt Android actual ContactsEntryContent wiring
feature/messaging/src/androidMain/kotlin/org/meshtastic/feature/messaging/Message.kt Uses multiplatform clipboard entry factory and encodeToByteArray()
feature/messaging/build.gradle.kts Pulls paging-compose + navigationevent into commonMain; simplifies androidMain deps
feature/map/src/jvmMain/kotlin/org/meshtastic/feature/map/navigation/MapMainScreen.kt Desktop placeholder actual map main screen
feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt Switches to ioDispatcher
feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/navigation/MapNavigation.kt Introduces expect MapMainScreen entry point and uses it
feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/navigation/MapMainScreen.kt Android actual wiring to MapScreen/SharedMapViewModel
feature/map/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
feature/intro/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
feature/firmware/src/jvmMain/kotlin/org/meshtastic/feature/firmware/navigation/FirmwareScreen.kt Desktop actual firmware screen
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/navigation/FirmwareScreen.kt Android actual firmware screen
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/navigation/FirmwareNavigation.kt Uses FirmwareScreen via expect/actual; exposes expect declaration
feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ScannerViewModel.kt Uses ioDispatcher for flowOn
feature/connections/src/androidMain/kotlin/org/meshtastic/feature/connections/navigation/ConnectionsNavigation.kt Uses shared ScannerViewModel instead of Android-specific VM
feature/connections/detekt-baseline.xml Cleans/empties detekt baseline
feature/connections/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
docs/roadmap.md Updates roadmap items to include iOS CI gate and shared settings status
docs/kmp-status.md Updates KMP status wording to reflect fully shared graphs
docs/decisions/navigation3-parity-2026-03.md Updates source anchors to reflect shared graph registration
docs/agent-playbooks/task-playbooks.md Updates Navigation 3 wiring playbook for shared graphs + expect/actual
docs/agent-playbooks/di-navigation3-anti-patterns-playbook.md Updates anchors for shared saved-state config and shared graph pattern
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopSettingsScreen.kt Moves desktop settings UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopSecurityConfigScreen.kt Moves desktop settings UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopPositionConfigScreen.kt Moves desktop settings UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopNetworkConfigScreen.kt Removes desktop-specific network config screen (now shared)
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopExternalNotificationConfigScreen.kt Moves desktop settings UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/settings/DesktopDeviceConfigScreen.kt Moves desktop settings UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/nodes/DesktopAdaptiveNodeListScreen.kt Removes desktop-specific node list screen (now shared graphs + wrappers)
desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopMessageContent.kt Removes desktop-specific message content (now shared MessageScreen)
desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopAdaptiveContactsScreen.kt Removes desktop-specific contacts screen (now shared AdaptiveContactsScreen)
desktop/src/main/kotlin/org/meshtastic/desktop/ui/firmware/DesktopFirmwareScreen.kt Moves desktop firmware UI into feature package namespace
desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt Removes desktop-local saved state config (now shared in core:navigation)
desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopSettingsNavigation.kt Removes desktop-specific settings graph wiring (now shared settingsGraph)
desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNodeNavigation.kt Removes desktop-specific node graph wiring (now shared nodesGraph)
desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt Assembles desktop graph from feature-provided shared graphs
desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopMessagingNavigation.kt Removes desktop-specific messaging graph wiring (now shared contactsGraph)
desktop/src/main/kotlin/org/meshtastic/desktop/Main.kt Uses shared MeshtasticNavSavedStateConfig
core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/util/NoopStubs.kt Adds iOS stubs for clipboard, NFC, toast, maps, URL, brightness
core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/theme/NoopStubs.kt Adds iOS stub for dynamic color scheme
core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/component/NoopStubs.kt Adds iOS stubs for time ticking and enum helpers
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt Updates preview annotations to Compose tooling preview light/dark
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/SignalInfo.kt Uses formatString for KMP-safe formatting
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/ScrollToTopEvent.kt Adds rememberScrollToTopEvents helper
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MaterialBatteryInfo.kt Uses formatString for KMP-safe formatting
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/LoraSignalIndicator.kt Uses formatString for KMP-safe formatting
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EditTextPreference.kt Uses encodeToByteArray() for KMP-friendly byte sizing
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/DropDownPreference.kt Adds JVM annotation import to support bridging (context within file)
core/ui/build.gradle.kts Adjusts tooling preview dependency exposure and jvmAndroid-only tooling
core/testing/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt Uses atomic+Mutex for listener initialization synchronization
core/service/build.gradle.kts Adds atomicfu dependency
core/resources/src/commonMain/kotlin/org/meshtastic/core/resources/GetString.kt Uses formatString for KMP-safe formatting
core/resources/build.gradle.kts Adds dependency on core:common to support formatString
core/repository/src/iosMain/kotlin/org/meshtastic/core/repository/Location.kt Adds iOS no-op Location stub
core/repository/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/proto/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/prefs/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/nfc/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/network/src/commonMain/kotlin/org/meshtastic/core/network/radio/MockInterface.kt Removes Integer.toHexString usage for KMP friendliness
core/network/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt Adds shared Navigation 3 saved-state serializer config
core/navigation/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/model/src/iosMain/kotlin/org/meshtastic/core/model/util/NoopStubs.kt Adds iOS stubs for platform-specific model utilities
core/model/src/commonMain/kotlin/org/meshtastic/core/model/util/SharedContact.kt Uses e::class.simpleName for KMP
core/model/src/commonMain/kotlin/org/meshtastic/core/model/util/DistanceExtensions.kt Uses formatString and refactors formatting logic
core/model/src/commonMain/kotlin/org/meshtastic/core/model/Node.kt Uses formatString for KMP-safe formatting
core/model/src/commonMain/kotlin/org/meshtastic/core/model/DataPacket.kt Uses formatString for KMP-safe formatting
core/model/src/commonMain/kotlin/org/meshtastic/core/model/Channel.kt Uses copyOf() instead of clone() for KMP friendliness
core/domain/build.gradle.kts Removes explicit jvm() target (now standardized elsewhere)
core/di/src/commonMain/kotlin/org/meshtastic/core/di/di/CoreDiModule.kt Provides CoroutineDispatchers.io using ioDispatcher
core/di/build.gradle.kts Adds dependency on core:common for ioDispatcher
core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/di/CoreDatastoreModule.kt Uses ioDispatcher for DataStore scope
core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/UiPreferencesDataSource.kt Uses ioDispatcher for backing scope
core/database/src/iosMain/kotlin/org/meshtastic/core/database/DatabaseBuilder.kt Reworks iOS DataStore creation using Okio storage
core/database/src/commonMain/kotlin/org/meshtastic/core/database/MeshtasticDatabase.kt Sets Room query dispatcher via ioDispatcher; updates migration annotations
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt Uses ioDispatcher for internal scope
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt Uses ioDispatcher for internal scope
core/common/src/jvmAndroidMain/kotlin/org/meshtastic/core/common/util/Formatter.kt JVM/Android implementation of formatString
core/common/src/jvmAndroidMain/kotlin/org/meshtastic/core/common/util/Dispatchers.kt JVM/Android ioDispatcher maps to Dispatchers.IO
core/common/src/iosMain/kotlin/org/meshtastic/core/common/util/NoopStubs.kt Adds iOS stubs for common utilities to allow compilation
core/common/src/iosMain/kotlin/org/meshtastic/core/common/util/Formatter.kt iOS stub implementation of formatString
core/common/src/iosMain/kotlin/org/meshtastic/core/common/util/Dispatchers.kt iOS ioDispatcher maps to Dispatchers.Default
core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/HomoglyphCharacterStringTransformer.kt Simplifies map substitution logic
core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/Formatter.kt Adds expect fun formatString
core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/Dispatchers.kt Adds expect val ioDispatcher
core/ble/src/iosMain/kotlin/org/meshtastic/core/ble/NoopStubs.kt Adds iOS BLE stubs for compilation
build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt Adds iOS KMP targets and shared opt-ins/compiler flags
app/src/main/kotlin/org/meshtastic/app/ui/Main.kt Uses shared Navigation 3 saved-state config
app/detekt-baseline.xml Cleans/empties detekt baseline
.jdk Adds local JDK path configuration file
.gitignore Updates ignored entries (appears to adjust firebase debug log ignore)
.github/workflows/reusable-check.yml Expands KMP smoke compile to include iOS simulator target
Comments suppressed due to low confidence (5)

.jdk:1

  • This file hard-codes a developer-specific absolute path. Committing it will break other environments and can cause tooling churn in PRs. Consider removing .jdk from the repo and adding it to .gitignore, or replacing it with a team-agnostic approach (e.g., documenting required JDK version and relying on toolchains).
    core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/ScrollToTopEvent.kt:1
  • A default MutableSharedFlow() has no buffer; emit() will suspend if there are no collectors, which can deadlock UI coroutines depending on how events are produced. Consider configuring buffering (e.g., MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)) and/or returning a SharedFlow plus a separate emitter API to avoid exposing mutable flow internals.
    core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/ScrollToTopEvent.kt:1
  • A default MutableSharedFlow() has no buffer; emit() will suspend if there are no collectors, which can deadlock UI coroutines depending on how events are produced. Consider configuring buffering (e.g., MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)) and/or returning a SharedFlow plus a separate emitter API to avoid exposing mutable flow internals.
    fix_dispatchers.py:1
  • This looks like a one-off migration script that rewrites source files in-place. Consider moving it under a clearly designated tooling/scripts directory (and documenting usage), or removing it from the repo once the migration is complete to avoid shipping ad-hoc code alongside production sources.
    fix_dispatchers.py:1
  • This looks like a one-off migration script that rewrites source files in-place. Consider moving it under a clearly designated tooling/scripts directory (and documenting usage), or removing it from the repo once the migration is complete to avoid shipping ad-hoc code alongside production sources.

* Update iOS `formatString` stub to explicitly throw an UnsupportedOperationException rather than silently returning the unformatted pattern string.
* Ensure the iOS DataStore stubs securely return `emptyPreferences()` rather than immediately crashing during app initialization.
* Refactor `KotlinAndroid.kt` convention plugin to configure Kotlin compilerOptions lazily rather than eagerly resolving the task provider.
…updates

* Updated GEMINI.md, AGENTS.md, and GitHub Copilot instructions to reflect iOS targets, thin Desktop shell architecture, and deduplicated UI.
* Updated desktop/README.md to detail unified Navigation 3 configuration, shared graphs, and the reduction of the desktop target to a shell module.
* Updated agent playbooks to explicitly forbid custom predictive back handlers in favor of Compose Multiplatform's NavigationBackHandler.
* Cleansed leftover markdown formatting artifacts from previous playbook modifications.
* Updated the Navigation 3 Parity Decision document to 'Implemented' and summarized the unification results.
* Moved formatMuteRemainingTime to commonMain to resolve undefined references in KMP UI.
* Fixed unresolved layout fillMaxSize import in Desktop MapMainScreen placeholder.
* Re-added explicit import to resolve duration issues in AndroidDateTimeUtils.
* Fixed unresolved ContactsViewModel references in Desktop navigation stubs.
* Completely replaced custom  wrapper invocations with native Compose Multiplatform resource formatting capabilities, utilizing the new  APIs added in recent Compose releases.
* Removed explicit formatting wrappers from , drastically simplifying synchronous string lookups.
* Migrated localized resource loading in  and  to run inside coroutines via  instead of performing blocking IO on the main execution thread.
…utines

* Updated core AI/developer guides (AGENTS.md, GEMINI.md, copilot-instructions.md) to explicitly mandate the usage of `getStringSuspend` inside ViewModels and Coroutine scopes.
* Updated `common-practices.md` to point to the newly relocated `NodeListScreen.kt` example in `commonMain`.
…lementation

- Migrate to `LocalBarcodeScannerProvider` and `LocalNfcScannerProvider` in network settings.
- Refactor `NetworkConfigScreen` to simplify UI logic, improve IP address formatting, and consolidate IPv4 configuration.
- Update desktop `NodeDetailScreen` to use `uiState` collection and a unified action handler.
- Add firmware feature dependency to the desktop module.
- Clean up unused imports and qualified names across node and settings features.
@jamesarich jamesarich requested a review from Copilot March 21, 2026 22:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 156 out of 158 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

.jdk:1

  • Committing a .jdk file with an absolute developer-local path will break portability for other contributors/CI environments and can leak user/workstation details. This should be removed from version control and added to .gitignore (or replaced by a repo-relative/tooling-agnostic JDK configuration if the project wants to standardize local JDK setup).
    core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt:1
  • There’s a race window where multiple calls can pass the initial listenersInitialized.value check and each enqueue a coroutine, even though only one will win the mutex. Consider atomically preventing multiple launches (e.g., if (!listenersInitialized.compareAndSet(false, true)) return before launching), or otherwise ensure only one init coroutine is scheduled.
    core/ui/build.gradle.kts:1
  • Exposing preview tooling as an api dependency makes it transitively available to downstream modules and can increase compile classpaths unnecessarily. Typically preview artifacts are implementation (or even debug-only) because they’re only needed for preview annotations. Consider switching api(...) to implementation(...) unless consumers truly need it on their public API surface.
    core/ui/build.gradle.kts:1
  • Exposing preview tooling as an api dependency makes it transitively available to downstream modules and can increase compile classpaths unnecessarily. Typically preview artifacts are implementation (or even debug-only) because they’re only needed for preview annotations. Consider switching api(...) to implementation(...) unless consumers truly need it on their public API surface.
    core/database/src/iosMain/kotlin/org/meshtastic/core/database/DatabaseBuilder.kt:1
  • This iOS DataStore implementation silently discards persisted preferences (always reads empty, writes no-op). Even for 'compile-only' iOS support, this is a footgun because it can fail at runtime without obvious signals. Prefer failing fast with an explicit UnsupportedOperationException (or log warnings) so any accidental runtime execution is immediately diagnosable.

…ading

- Introduce `getAboutLibrariesJson()` as an expect/actual function to handle platform-specific library resource loading.
- Add iOS navigation stubs for Map, Messaging, Firmware, and Settings screens.
- Refine iOS no-op stub signatures in `core:ui` and `feature:settings` for better parameter clarity.
@jamesarich jamesarich added this pull request to the merge queue Mar 21, 2026
Merged via the queue into main with commit d136b16 Mar 21, 2026
8 checks passed
@jamesarich jamesarich deleted the feat/ios-gates branch March 21, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants