Skip to content

Commit edc3f8d

Browse files
committed
feat(ios): add state restoration across app lifecycle
1 parent 1bb338c commit edc3f8d

4 files changed

Lines changed: 43 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- iPad keyboard shortcuts (Cmd+N new connection, Cmd+Return execute query, Cmd+1/2 switch tabs) and trackpad hover effects on list rows
1414
- Server Dashboard with active sessions, server metrics, and slow query monitoring (PostgreSQL, MySQL, MSSQL, ClickHouse, DuckDB, SQLite)
1515
- Handoff support for cross-device continuity between iOS and macOS
16+
- State restoration across app lifecycle on iOS (selected connection, active tab, query text, database/schema selection)
1617

1718
## [0.30.1] - 2026-04-10
1819

TableProMobile/TableProMobile/Views/ConnectedView.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ struct ConnectedView: View {
2121
@State private var appError: AppError?
2222
@State private var failureAlertMessage: String?
2323
@State private var showFailureAlert = false
24-
@State private var selectedTab = ConnectedTab.tables
24+
@SceneStorage("selectedTab") private var selectedTabRaw: String = ConnectedTab.tables.rawValue
2525
@State private var queryHistory: [QueryHistoryItem] = []
2626
@State private var historyStorage = QueryHistoryStorage()
2727
@State private var databases: [String] = []
28-
@State private var activeDatabase: String = ""
28+
@SceneStorage("activeDatabase") private var activeDatabase: String = ""
2929
@State private var schemas: [String] = []
30-
@State private var activeSchema: String = "public"
30+
@SceneStorage("activeSchema") private var activeSchema: String = "public"
3131
@State private var isSwitching = false
3232
@State private var isReconnecting = false
3333
@State private var hapticSuccess = false
@@ -40,6 +40,18 @@ struct ConnectedView: View {
4040
case query = "Query"
4141
}
4242

43+
private var selectedTab: ConnectedTab {
44+
get { ConnectedTab(rawValue: selectedTabRaw) ?? .tables }
45+
set { selectedTabRaw = newValue.rawValue }
46+
}
47+
48+
private var selectedTabBinding: Binding<ConnectedTab> {
49+
Binding(
50+
get: { ConnectedTab(rawValue: selectedTabRaw) ?? .tables },
51+
set: { selectedTabRaw = $0.rawValue }
52+
)
53+
}
54+
4355
private var displayName: String {
4456
connection.name.isEmpty ? connection.host : connection.name
4557
}
@@ -119,7 +131,7 @@ struct ConnectedView: View {
119131
.navigationTitle(supportsDatabaseSwitching && databases.count > 1 ? "" : displayName)
120132
.navigationBarTitleDisplayMode(.inline)
121133
.safeAreaInset(edge: .top) {
122-
Picker("Tab", selection: $selectedTab) {
134+
Picker("Tab", selection: selectedTabBinding) {
123135
Text("Tables").tag(ConnectedTab.tables)
124136
Text("Query").tag(ConnectedTab.query)
125137
}

TableProMobile/TableProMobile/Views/ConnectionListView.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ struct ConnectionListView: View {
1212
@Environment(\.horizontalSizeClass) private var sizeClass
1313
@State private var showingAddConnection = false
1414
@State private var editingConnection: DatabaseConnection?
15-
@State private var selectedConnectionId: UUID?
15+
@SceneStorage("selectedConnectionId") private var selectedConnectionIdString: String?
1616
@State private var columnVisibility: NavigationSplitViewVisibility = .automatic
1717
@State private var showingGroupManagement = false
1818
@State private var showingTagManagement = false
19-
@State private var filterTagId: UUID?
20-
@State private var groupByGroup = false
19+
@SceneStorage("filterTagId") private var filterTagIdString: String?
20+
@AppStorage("groupByGroup") private var groupByGroup = false
2121
@State private var editMode: EditMode = .inactive
2222
@State private var connectionToDelete: DatabaseConnection?
2323

@@ -28,6 +28,22 @@ struct ConnectionListView: View {
2828
)
2929
}
3030

31+
private var selectedConnectionId: Binding<UUID?> {
32+
Binding(
33+
get: { selectedConnectionIdString.flatMap { UUID(uuidString: $0) } },
34+
set: { selectedConnectionIdString = $0?.uuidString }
35+
)
36+
}
37+
38+
private var selectedConnectionUUID: UUID? {
39+
selectedConnectionIdString.flatMap { UUID(uuidString: $0) }
40+
}
41+
42+
private var filterTagId: UUID? {
43+
get { filterTagIdString.flatMap { UUID(uuidString: $0) } }
44+
set { filterTagIdString = newValue?.uuidString }
45+
}
46+
3147
private var displayedConnections: [DatabaseConnection] {
3248
var result = appState.connections
3349
if let filterTagId {
@@ -44,8 +60,8 @@ struct ConnectionListView: View {
4460
}
4561

4662
private var selectedConnection: DatabaseConnection? {
47-
guard let selectedConnectionId else { return nil }
48-
return appState.connections.first { $0.id == selectedConnectionId }
63+
guard let id = selectedConnectionUUID else { return nil }
64+
return appState.connections.first { $0.id == id }
4965
}
5066

5167
var body: some View {
@@ -90,7 +106,7 @@ struct ConnectionListView: View {
90106
.onChange(of: appState.pendingConnectionId) { _, newId in
91107
navigateToPendingConnection(newId)
92108
}
93-
.onChange(of: filterTagId) {
109+
.onChange(of: filterTagIdString) {
94110
editMode = .inactive
95111
}
96112
.onChange(of: groupByGroup) {
@@ -201,8 +217,8 @@ struct ConnectionListView: View {
201217
) {
202218
Button(String(localized: "Delete"), role: .destructive) {
203219
if let connection = connectionToDelete {
204-
if selectedConnectionId == connection.id {
205-
selectedConnectionId = nil
220+
if selectedConnectionUUID == connection.id {
221+
selectedConnectionIdString = nil
206222
}
207223
appState.removeConnection(connection)
208224
}
@@ -339,7 +355,7 @@ struct ConnectionListView: View {
339355
private func navigateToPendingConnection(_ id: UUID?) {
340356
guard let id,
341357
appState.connections.contains(where: { $0.id == id }) else { return }
342-
selectedConnectionId = id
358+
selectedConnectionIdString = id.uuidString
343359
appState.pendingConnectionId = nil
344360
}
345361

TableProMobile/TableProMobile/Views/QueryEditorView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct QueryEditorView: View {
1717

1818
private static let logger = Logger(subsystem: "com.TablePro", category: "QueryEditorView")
1919

20-
@State private var query = ""
20+
@SceneStorage("queryText") private var query = ""
2121
@State private var result: QueryResult?
2222
@State private var appError: AppError?
2323
@State private var isExecuting = false

0 commit comments

Comments
 (0)