Skip to content

Commit 97eadaf

Browse files
committed
Add search tab on OS 26+
1 parent 088c961 commit 97eadaf

2 files changed

Lines changed: 116 additions & 1 deletion

File tree

SerialBridge/Views/ConnectedView.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,33 @@ struct ConnectedView: View {
1212
@Binding var selectedTab: Int
1313

1414
var body: some View {
15-
TabView(selection: $selectedTab) {
15+
if #available(iOS 26.0, visionOS 26.0, *) {
16+
TabView {
17+
Tab {
18+
PlotView(networkManager: networkManager)
19+
} label: {
20+
Label("Plot", systemImage: "chart.bar")
21+
}
22+
23+
Tab {
24+
LogView(networkManager: networkManager)
25+
} label: {
26+
Label("Log", systemImage: "list.bullet")
27+
}
28+
29+
Tab {
30+
ConnectionInfoView(networkManager: networkManager)
31+
} label: {
32+
Label("Sync", systemImage: "macbook.and.iphone")
33+
}
34+
35+
Tab(role: .search) {
36+
NavigationStack {
37+
SearchView(networkManager: networkManager)
38+
}
39+
}
40+
}
41+
} else {
1642
PlotView(networkManager: networkManager)
1743
.tabItem {
1844
Image(systemName: "chart.bar")
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// SearchView.swift
3+
// SerialBridge
4+
//
5+
// Created by ash on 8/31/25.
6+
//
7+
8+
import SwiftUI
9+
10+
struct SearchView: View {
11+
@ObservedObject var networkManager: MobileNetworkManager
12+
@State var searchText: String = ""
13+
14+
var filteredReadings: [DeviceReading] {
15+
if searchText.isEmpty {
16+
return networkManager.readings
17+
} else {
18+
return networkManager.readings.filter { reading in
19+
reading.values.keys.contains { $0.localizedCaseInsensitiveContains(searchText) } ||
20+
reading.values.values.contains { "\($0)".localizedCaseInsensitiveContains(searchText) }
21+
}
22+
}
23+
}
24+
25+
var filteredLogs: [String] {
26+
if searchText.isEmpty {
27+
return networkManager.logLines
28+
} else {
29+
return networkManager.logLines.filter { $0.localizedCaseInsensitiveContains(searchText) }
30+
}
31+
}
32+
33+
var body: some View {
34+
NavigationView {
35+
List {
36+
if networkManager.logLines.isEmpty {
37+
PlaceholderItem(
38+
systemImage: "doc.text",
39+
systemImageColor: Color.yellow,
40+
title: "No Data Yet",
41+
subtitle: "Start monitoring to search data."
42+
)
43+
.frame(height: 250)
44+
} else {
45+
Section("Values") {
46+
if let lastReading = filteredReadings.last {
47+
let keys = lastReading.values.keys.sorted()
48+
ForEach(Array(keys.enumerated()), id: \.element) { index, key in
49+
if let value = lastReading.values[key] {
50+
if searchText.isEmpty ||
51+
key.localizedCaseInsensitiveContains(searchText) ||
52+
"\(value)".localizedCaseInsensitiveContains(searchText) {
53+
VStack(alignment: .leading) {
54+
Text(key)
55+
Text("\(value)")
56+
.font(.caption)
57+
.foregroundStyle(.secondary)
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}
64+
65+
Section("Logs") {
66+
ForEach(Array(filteredLogs.enumerated()), id: \.offset) { index, line in
67+
Text(line)
68+
.font(.system(.caption, design: .monospaced))
69+
.padding(.vertical, 1)
70+
.id(index)
71+
}
72+
}
73+
}
74+
75+
HStack {
76+
Text("\(networkManager.readings.count) readings")
77+
Spacer()
78+
Text("\(networkManager.logLines.count) log lines")
79+
}
80+
.font(.caption)
81+
.foregroundStyle(.secondary)
82+
.padding()
83+
}
84+
.searchable(text: $searchText)
85+
.navigationTitle("Search")
86+
.modifier(NavigationSubtitleIfAvailable(subtitle: "Data"))
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)