Skip to content

Commit a24d834

Browse files
garthvhCopilotCopilot
authored
Performance optimizations: formatter caching, view extraction, query limits (#1763)
* Performance optimizations: formatter caching, view extraction, query limits - CoTMessage: Make ISO8601DateFormatter a static let instead of creating per toXML() call - DiscoveryScanEngine: Add CustomStringConvertible to DiscoveryScanState, remove String(describing:) wrapper from log interpolations - CarPlaySceneDelegate: Move favorite filter into #Predicate and add fetchLimit: 50 to avoid loading all nodes - RateLimitedButton: Move objectWillChange.send() after cleanup check to avoid unnecessary view redraws after timer stops - NodeDetail: Extract 600-line body into 6 @ViewBuilder section properties for granular SwiftUI diffing * Fix persistent tip disappearing after 3 displays Remove Tips.MaxDisplayCount(3) from ConnectionTip. The tip uses PersistentTipStyle which has no dismiss button, so it should always appear when connected. The max count caused TipKit to internally invalidate the tip after 3 views, rendering it invisible. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Use thread-safe CoT XML date formatting Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/0867e03f-148b-4ec7-9e09-2162d76419b5 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Fix review feedback in node admin and rate limiting Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/a031fb01-f381-413c-a593-5333638d7774 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Polish node administration guard logic Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/a031fb01-f381-413c-a593-5333638d7774 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
1 parent fb4895b commit a24d834

7 files changed

Lines changed: 617 additions & 518 deletions

File tree

Meshtastic/CarPlay/CarPlaySceneDelegate.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,13 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPI
182182

183183
private func fetchFavoriteContactItems() -> [CPMessageListItem] {
184184
do {
185-
let descriptor = FetchDescriptor<NodeInfoEntity>(
185+
let activeNum = Int64(AccessoryManager.shared.activeDeviceNum ?? 0)
186+
var descriptor = FetchDescriptor<NodeInfoEntity>(
187+
predicate: #Predicate<NodeInfoEntity> { $0.favorite == true && $0.num != activeNum },
186188
sortBy: [SortDescriptor(\.lastHeard, order: .reverse)]
187189
)
188-
let activeNum = AccessoryManager.shared.activeDeviceNum ?? 0
189-
let nodes = try context.fetch(descriptor).filter { $0.favorite && $0.num != activeNum }
190+
descriptor.fetchLimit = 50
191+
let nodes = try context.fetch(descriptor)
190192
let nodeNums = nodes.compactMap { $0.user != nil ? $0.num : nil as Int64? }
191193
let unreadCounts = fetchUnreadCountsForDMs(nodeNums: nodeNums)
192194
let now = Date()

Meshtastic/Helpers/TAK/CoTMessage.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,17 @@ struct CoTMessage: Identifiable, Sendable {
282282
// MARK: - XML Generation
283283

284284
/// Generate CoT XML string for transmission to TAK clients
285+
private static let xmlDateFormatStyle = Date.ISO8601FormatStyle(includingFractionalSeconds: true, timeZone: TimeZone(secondsFromGMT: 0)!)
286+
285287
func toXML() -> String {
286-
let dateFormatter = ISO8601DateFormatter()
287-
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
288+
let dateFormatter = Self.xmlDateFormatStyle
288289

289290
var cot = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
290291
cot += "<event version='2.0' uid='\(uid.xmlEscaped)' "
291292
cot += "type='\(type)' "
292-
cot += "time='\(dateFormatter.string(from: time))' "
293-
cot += "start='\(dateFormatter.string(from: start))' "
294-
cot += "stale='\(dateFormatter.string(from: stale))' "
293+
cot += "time='\(time.formatted(dateFormatter))' "
294+
cot += "start='\(start.formatted(dateFormatter))' "
295+
cot += "stale='\(stale.formatted(dateFormatter))' "
295296
cot += "how='\(how)'>"
296297
cot += "<point lat='\(latitude)' lon='\(longitude)' "
297298
cot += "hae='\(hae)' ce='\(ce)' le='\(le)'/>"

Meshtastic/Services/DiscoveryScanEngine.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SwiftUI
1616

1717
// MARK: - Scan State
1818

19-
enum DiscoveryScanState: Equatable {
19+
enum DiscoveryScanState: Equatable, CustomStringConvertible {
2020
case idle
2121
case shifting
2222
case reconnecting
@@ -25,6 +25,19 @@ enum DiscoveryScanState: Equatable {
2525
case complete
2626
case paused
2727
case restoring
28+
29+
var description: String {
30+
switch self {
31+
case .idle: "idle"
32+
case .shifting: "shifting"
33+
case .reconnecting: "reconnecting"
34+
case .dwell: "dwell"
35+
case .analysis: "analysis"
36+
case .complete: "complete"
37+
case .paused: "paused"
38+
case .restoring: "restoring"
39+
}
40+
}
2841
}
2942

3043
// MARK: - DiscoveryScanEngine
@@ -91,7 +104,7 @@ final class DiscoveryScanEngine {
91104

92105
func startScan() async {
93106
guard currentState == .idle else {
94-
Logger.discovery.warning("📡 [Discovery] Cannot start scan — not idle (state: \(String(describing: self.currentState)))")
107+
Logger.discovery.warning("📡 [Discovery] Cannot start scan — not idle (state: \(self.currentState))")
95108
return
96109
}
97110
guard !selectedPresets.isEmpty else {
@@ -688,7 +701,7 @@ extension DiscoveryScanEngine {
688701
private func transitionTo(_ newState: DiscoveryScanState) {
689702
let oldState = currentState
690703
currentState = newState
691-
Logger.discovery.info("📡 [Discovery] State: \(String(describing: oldState))\(String(describing: newState))")
704+
Logger.discovery.info("📡 [Discovery] State: \(oldState)\(newState)")
692705
}
693706

694707
private func cleanupAndIdle() {

Meshtastic/Tips/BluetoothTips.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,5 @@ struct ConnectionTip: Tip {
2323
}
2424
var options: [TipOption] {
2525
Tips.IgnoresDisplayFrequency(true)
26-
Tips.MaxDisplayCount(3)
2726
}
2827
}

Meshtastic/Views/Helpers/RateLimitedButton.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,17 @@ class RateLimitStorage: ObservableObject {
108108
// Create the timer
109109
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
110110
guard let self = self else { return }
111-
self.objectWillChange.send()
112111

113112
// Determine if we can clean up the dictionary and stop the timer.
114113
let maxExpiration = self.rateLimits.values.map { $0.rateLimitExpires }.max() ?? .distantPast
115114
if maxExpiration.timeIntervalSinceNow < 0 {
116-
// All rateLimits are in the past. Stop and clean up
115+
// All rateLimits are in the past. Stop and clean up
117116
self.timer?.invalidate()
118117
self.timer = nil
119118
self.rateLimits.removeAll()
119+
return
120120
}
121+
self.objectWillChange.send()
121122
}
122123
}
123124
}

0 commit comments

Comments
 (0)