Skip to content

Commit ca42174

Browse files
authored
refactor: replace SSH tunnel and window lifecycle notifications with direct calls (#289)
* refactor: replace editor and AI notifications with direct calls * refactor: replace SSH tunnel and window lifecycle notifications with direct calls
1 parent 5bd984c commit ca42174

5 files changed

Lines changed: 16 additions & 72 deletions

File tree

TablePro/Core/Database/DatabaseManager.swift

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -66,46 +66,7 @@ final class DatabaseManager {
6666
currentSession?.status ?? .disconnected
6767
}
6868

69-
@ObservationIgnored nonisolated(unsafe) private var sshTunnelObserver: NSObjectProtocol?
70-
@ObservationIgnored nonisolated(unsafe) private var lastWindowCloseObserver: NSObjectProtocol?
71-
72-
private init() {
73-
// Observe SSH tunnel failures
74-
sshTunnelObserver = NotificationCenter.default.addObserver(
75-
forName: .sshTunnelDied,
76-
object: nil,
77-
queue: .main
78-
) { [weak self] notification in
79-
guard let connectionId = notification.userInfo?["connectionId"] as? UUID else { return }
80-
guard let self else { return }
81-
82-
Task { @MainActor in
83-
await self.handleSSHTunnelDied(connectionId: connectionId)
84-
}
85-
}
86-
87-
lastWindowCloseObserver = NotificationCenter.default.addObserver(
88-
forName: .lastWindowDidClose,
89-
object: nil,
90-
queue: .main
91-
) { [weak self] notification in
92-
guard let connectionId = notification.userInfo?["connectionId"] as? UUID else { return }
93-
guard let self else { return }
94-
95-
Task { @MainActor in
96-
await self.disconnectSession(connectionId)
97-
}
98-
}
99-
}
100-
101-
deinit {
102-
if let sshTunnelObserver {
103-
NotificationCenter.default.removeObserver(sshTunnelObserver)
104-
}
105-
if let lastWindowCloseObserver {
106-
NotificationCenter.default.removeObserver(lastWindowCloseObserver)
107-
}
108-
}
69+
private init() {}
10970

11071
// MARK: - Session Management
11172

@@ -622,7 +583,7 @@ final class DatabaseManager {
622583
// MARK: - SSH Tunnel Recovery
623584

624585
/// Handle SSH tunnel death by attempting reconnection with exponential backoff
625-
private func handleSSHTunnelDied(connectionId: UUID) async {
586+
func handleSSHTunnelDied(connectionId: UUID) async {
626587
guard let session = activeSessions[connectionId] else { return }
627588

628589
Self.logger.warning("SSH tunnel died for connection: \(session.connection.name)")

TablePro/Core/SSH/SSHTunnelManager.swift

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,7 @@ actor SSHTunnelManager {
9090
private func handleTunnelDeath(connectionId: UUID) async {
9191
guard tunnels.removeValue(forKey: connectionId) != nil else { return }
9292
Self.processRegistry.withLock { $0[connectionId] = nil }
93-
await notifyTunnelDied(connectionId: connectionId)
94-
}
95-
96-
/// Notify that a tunnel has died (DatabaseManager should handle reconnection)
97-
private func notifyTunnelDied(connectionId: UUID) async {
98-
await MainActor.run {
99-
NotificationCenter.default.post(
100-
name: .sshTunnelDied,
101-
object: nil,
102-
userInfo: ["connectionId": connectionId]
103-
)
104-
}
93+
await DatabaseManager.shared.handleSSHTunnelDied(connectionId: connectionId)
10594
}
10695

10796
/// Create an SSH tunnel for a database connection

TablePro/Core/Services/Infrastructure/AppNotifications.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,4 @@ extension Notification.Name {
1919
static let connectionUpdated = Notification.Name("connectionUpdated")
2020
static let databaseDidConnect = Notification.Name("databaseDidConnect")
2121

22-
// MARK: - SSH
23-
24-
static let sshTunnelDied = Notification.Name("sshTunnelDied")
25-
static let lastWindowDidClose = Notification.Name("lastWindowDidClose")
2622
}

TablePro/Core/Services/Infrastructure/WindowLifecycleMonitor.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,9 @@ internal final class WindowLifecycleMonitor {
149149

150150
let hasRemainingWindows = entries.values.contains { $0.connectionId == closedConnectionId }
151151
if !hasRemainingWindows {
152-
NotificationCenter.default.post(
153-
name: .lastWindowDidClose,
154-
object: nil,
155-
userInfo: ["connectionId": closedConnectionId]
156-
)
152+
Task {
153+
await DatabaseManager.shared.disconnectSession(closedConnectionId)
154+
}
157155
}
158156
}
159157
}

docs/development/notification-refactor.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,20 @@ Replaced 7 notifications with direct calls. Editor notifications use `@FocusedVa
194194

195195
## Phase 7: Replace Window Lifecycle Notifications
196196

197-
**Status:** Not started
197+
**Status:** Done
198+
199+
Replaced 2 singleton-to-singleton notifications with direct method calls. `SSHTunnelManager` calls `DatabaseManager.shared.handleSSHTunnelDied(connectionId:)` directly. `WindowLifecycleMonitor` calls `DatabaseManager.shared.disconnectSession(_:)` directly. Removed notification observers and cleanup from `DatabaseManager`.
198200

199-
### Keep (AppKit → SwiftUI bridge, no alternative):
201+
### Replaced:
200202

201-
- `openMainWindow``AppDelegate` → SwiftUI `openWindow`
202-
- `openWelcomeWindow` — same
203-
- `mainWindowWillClose``NSWindowDelegate` → tab persistence
203+
- [x] `lastWindowDidClose``WindowLifecycleMonitor` calls `DatabaseManager.shared.disconnectSession(_:)` directly
204+
- [x] `sshTunnelDied``SSHTunnelManager` calls `DatabaseManager.shared.handleSSHTunnelDied(connectionId:)` directly
204205

205-
### Replace:
206+
### Kept (cross-scene broadcasts, no shared reference):
206207

207-
- [ ] `lastWindowDidClose``WindowLifecycleMonitor``DatabaseManager`. Use a direct callback/delegate.
208-
- [ ] `sshTunnelDied``SSHTunnelManager``DatabaseManager`. Use a closure callback set at tunnel creation.
209-
- [ ] `connectionUpdated``ConnectionFormView``WelcomeWindowView`. Use `@Observable ConnectionStorage`.
210-
- [ ] `newConnection` — menu → welcome/content view. Use `@FocusedValue` or `@Environment(\.openWindow)`.
208+
- `connectionUpdated``ConnectionFormView`/`AppDelegate``WelcomeWindowView`
209+
- `newConnection``TableProApp``WelcomeWindowView`/`ContentView`
210+
- `databaseDidConnect``DatabaseManager``MainContentCommandActions`
211211

212212
---
213213

0 commit comments

Comments
 (0)