Skip to content

feat: open SQLite files from Finder by double-clicking#263

Merged
datlechin merged 4 commits intomainfrom
feat/open-sqlite-files
Mar 11, 2026
Merged

feat: open SQLite files from Finder by double-clicking#263
datlechin merged 4 commits intomainfrom
feat/open-sqlite-files

Conversation

@datlechin
Copy link
Copy Markdown
Collaborator

@datlechin datlechin commented Mar 11, 2026

Closes #262

Summary

  • Register SQLite file types (.sqlite, .sqlite3, .db3, .s3db, .sl3, .sqlitedb) in Info.plist with LSHandlerRank = Alternate so TablePro appears in Finder's "Open With" menu
  • Add UTImportedTypeDeclarations for com.tablepro.sqlite-db covering extensions not in Apple's system UTI
  • Handle SQLite file open events in AppDelegate — creates a transient SQLite connection using the file path, with deduplication against active sessions and cold-start queuing

Test plan

  • Create a test file: sqlite3 /tmp/test.sqlite "create table t(id integer);"
  • With app not running: double-click the file — app should launch and connect directly
  • With app running (no connection): double-click — welcome window closes, main window opens
  • With app running (connected): double-click — new tab/window opens with the SQLite file
  • Double-click same file again — should bring existing window to front (deduplication)
  • Test .sqlite3, .db3, .s3db, .sl3, .sqlitedb extensions
  • Right-click a .sqlite file — TablePro should appear in "Open With"

Summary by CodeRabbit

  • New Features

    • Double-click to open SQLite files (.sqlite, .sqlite3, .db3, .s3db, .sl3, .sqlitedb) from Finder — opens or focuses the relevant DB window/tab.
    • Handle database URL deeplinks (connect, openTable, openQuery, importConnection) and plugin install files; SQL files queue until a session is ready.
    • Dock menu: “Show Welcome Window” and “Open Connection” submenu for quick access.
  • Quality of Life

    • Improved welcome-window suppression, window/tab behavior, and auto-reconnect to reduce startup interruptions.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6e8e4d7f-492f-410e-bb0f-6d8d71f25f0c

📥 Commits

Reviewing files that changed from the base of the PR and between 5b30cc6 and 6d7692c.

📒 Files selected for processing (3)
  • TablePro/AppDelegate+ConnectionHandler.swift
  • TablePro/AppDelegate.swift
  • TablePro/Info.plist

📝 Walkthrough

Walkthrough

Adds Finder double‑click support for SQLite files via Info.plist document/UTType declarations and refactors AppDelegate into extensions that queue and process opened URLs (database URLs, SQLite files, SQL files, deeplinks, plugin installs), managing window readiness, connection creation/reuse, post-connection actions, and failure handling.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Add Unreleased entry documenting direct-open support for SQLite files.
Info & Types
TablePro/Info.plist
Register "SQLite Database" document type and com.tablepro.sqlite-db UTImportedTypeDeclaration for extensions: sqlite, sqlite3, db3, s3db, sl3, sqlitedb.
App Delegate - Core
TablePro/AppDelegate.swift
Visibility/state changes: several previously-private properties made public and queued URL state consolidated.
App Delegate - Connection Handler
TablePro/AppDelegate+ConnectionHandler.swift
New queue-based connection handling: QueuedURLEntry enum, queue draining, transient connection builder, async connect flow, session lookup/reuse, post-connection actions, and connection failure cleanup.
App Delegate - File Open / Deeplinks / Plugins
TablePro/AppDelegate+FileOpen.swift
Centralized URL/file classification and handleOpenURLs flow for deeplinks, plugin installs, database URLs, SQLite files, and SQL files; welcome-window suppression and deeplink/import/plugin handling.
App Delegate - Window Configuration
TablePro/AppDelegate+WindowConfig.swift
Dock menu and reopen handling, welcome window orchestration, window style/tabbing management, auto-reconnect flow, and window lifecycle helpers.
Misc / Helpers
TablePro/AppDelegate+*.swift (new/modified extensions)
Multiple new helpers, loggers, notification handlers, and small utilities added across AppDelegate extensions for queueing, waiting for connections, and UI coordination.

Sequence Diagram

sequenceDiagram
    actor Finder as Finder / User
    participant AppDel as AppDelegate
    participant Queue as URL Queue
    participant Conn as Connection Manager
    participant UI as Editor / Windows

    Finder->>AppDel: open file URL (.sqlite, .db3, .sql, etc.)
    AppDel->>AppDel: classify URL (sqlite, db URL, sql, deeplink, plugin)
    AppDel->>Queue: enqueue if SwiftUI/windows not ready
    Note over Queue,AppDel: scheduleQueuedURLProcessing (poll readiness ≈5s)
    Queue->>AppDel: drain queued entries
    AppDel->>Conn: find matching session or build transient connection
    alt existing session
        AppDel->>UI: bring connection window to front / open tab
    else new connection
        AppDel->>Conn: initiate async connect
        Conn-->>AppDel: success / failure
        AppDel->>UI: open editor window, apply schema/tabs/filters on success
        AppDel->>UI: show error sheet or welcome on failure
    end
    AppDel->>AppDel: suppress/restore welcome window during processing
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I sniffed the Finder’s fragrant trail,
A double‑click — a tiny hopping tale.
I queued the URL, opened windows bright,
Tables unfurled in morning light.
Hooray — SQLite now hops into sight! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes substantial AppDelegate refactoring (splitting into extensions and simplifying URL handling logic) which goes beyond the specific requirement to add SQLite file-open support. Consider isolating the SQLite file-open feature in a separate PR, or document why the refactoring is a prerequisite for this feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: open SQLite files from Finder by double-clicking' directly and concisely summarizes the main objective of the pull request.
Linked Issues check ✅ Passed The pull request implements all core requirements from issue #262: registers SQLite extensions in Info.plist, implements file-open handling via AppDelegate, and enables double-click opening from Finder.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/open-sqlite-files

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TablePro/AppDelegate.swift`:
- Around line 205-217: The welcome-window suppression is scheduled immediately
even when handleSQLiteFile(_:) only queues opens; change the flow so suppression
isn't ended until queued SQLite opens are drained: update handleSQLiteFile(_:)
to return a Bool (or other signal) indicating "queued" versus "opened", or
expose a queued count accessor (e.g., queuedSQLiteFileCount or
scheduleQueuedSQLiteFileProcessing completion), then in the Task { `@MainActor`
... } loop record whether any call returned queued and only call
scheduleWelcomeWindowSuppression() after confirming no files remain queued (or
by awaiting scheduleQueuedSQLiteFileProcessing() to complete); keep using the
existing flags isHandlingFileOpen and fileOpenSuppressionCount but postpone
decrement/scheduling until the queued-open drain has completed.
- Around line 493-509: The current dedupe only rejects fully-connected sessions
(session.driver != nil); update the guard to also deduplicate when the same file
is already queued or is in an active session that is still connecting.
Specifically, before creating a new open request, check
queuedSQLiteFileURLs.contains(url) and check
DatabaseManager.shared.activeSessions for any session with
session.connection.type == .sqlite && session.connection.database == filePath
(regardless of session.driver), and short-circuit by returning (and call
bringConnectionWindowToFront(sessionId) only if that session/window exists)
instead of proceeding to create a duplicate; keep references to
WindowOpener.shared.openWindow, queuedSQLiteFileURLs,
DatabaseManager.shared.activeSessions, session.connection.database,
session.driver, bringConnectionWindowToFront, and
scheduleQueuedSQLiteFileProcessing to locate the relevant code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81fdee9f-1f22-46d7-b78a-31a951393f8e

📥 Commits

Reviewing files that changed from the base of the PR and between 7152055 and 4b75de4.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • TablePro/AppDelegate.swift
  • TablePro/Info.plist

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
TablePro/AppDelegate.swift (2)

494-512: ⚠️ Potential issue | 🟠 Major

Deduplication gaps remain: queued files and connecting sessions are not checked.

Three issues persist from prior review:

  1. Queue-level dedup missing (Line 497): The same file can be appended to queuedSQLiteFileURLs multiple times if opened repeatedly during cold start.

  2. Connecting sessions not deduplicated (Lines 507-508): The session.driver != nil condition only catches fully connected sessions. If the user double-clicks the same file while it's still connecting, a duplicate window/session is created.

  3. Path normalization absent (Line 502): Comparing url.path directly fails to deduplicate when the same file is opened via a symlink or different path casing.

🔁 Suggested fix
 `@MainActor`
 private func handleSQLiteFile(_ url: URL) {
+    let normalizedURL = url.standardizedFileURL.resolvingSymlinksInPath()
+    let filePath = normalizedURL.path
+
     guard WindowOpener.shared.openWindow != nil else {
-        queuedSQLiteFileURLs.append(url)
+        if !queuedSQLiteFileURLs.contains(where: {
+            $0.standardizedFileURL.resolvingSymlinksInPath() == normalizedURL
+        }) {
+            queuedSQLiteFileURLs.append(normalizedURL)
+        }
         scheduleQueuedSQLiteFileProcessing()
         return
     }

-    let filePath = url.path
     let connectionName = url.deletingPathExtension().lastPathComponent

     // Deduplicate: if this file is already open in an active session, bring it to front
     for (sessionId, session) in DatabaseManager.shared.activeSessions {
-        if session.connection.type == .sqlite && session.connection.database == filePath
-            && session.driver != nil {
+        if session.connection.type == .sqlite,
+           URL(fileURLWithPath: session.connection.database)
+               .standardizedFileURL
+               .resolvingSymlinksInPath()
+               .path == filePath {
             bringConnectionWindowToFront(sessionId)
             return
         }
     }

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate.swift` around lines 494 - 512, The handleSQLiteFile
function currently misses dedup checks for queued URLs and for sessions that are
still connecting, and it compares raw paths; fix it by canonicalizing the URL
before any comparisons (e.g., resolveSymlinks and standardized path/case into a
single filePath variable) then check queuedSQLiteFileURLs for an existing
canonicalized URL and skip appending if present, and update the active session
loop over DatabaseManager.shared.activeSessions to consider both fully connected
(session.driver != nil) and connecting sessions (session.connection.type ==
.sqlite && session.connection.database == canonicalFilePath) so duplicates are
not created while connecting; if a matching session exists, call
bringConnectionWindowToFront(sessionId) and return.

206-221: ⚠️ Potential issue | 🟠 Major

Welcome window suppression timing still misaligned with queued file processing.

When handleSQLiteFile(_:) queues files (because WindowOpener.shared.openWindow == nil), Line 219 immediately schedules suppression cleanup (~700ms). However, scheduleQueuedSQLiteFileProcessing() can poll for up to 5 seconds waiting for the SwiftUI window system. The suppression will end before queued files are processed, allowing the welcome window to reappear.

Consider deferring scheduleWelcomeWindowSuppression() until after confirming files were actually opened (not just queued), or have scheduleQueuedSQLiteFileProcessing() manage the suppression lifecycle when it drains the queue.
,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate.swift` around lines 206 - 221, The welcome-window
suppression is being scheduled immediately after queuing files, which can expire
before queued files are actually opened; update the flow so suppression covers
the entire queued-processing lifetime by either (A) deferring the call to
scheduleWelcomeWindowSuppression() until files are actually opened (move the
call into the code path that runs when WindowOpener.shared.openWindow returns a
non-nil window or when handleSQLiteFile(_:) confirms an opened window), or (B)
have scheduleQueuedSQLiteFileProcessing() (the routine that polls for SwiftUI
windows) take ownership of the suppression lifecycle: increment
isHandlingFileOpen/fileOpenSuppressionCount when queuing starts and
decrement/schedule cleanup only when the queue is drained and all queued files
processed. Ensure you reference handleSQLiteFile(_:),
scheduleWelcomeWindowSuppression(), scheduleQueuedSQLiteFileProcessing(),
WindowOpener.shared.openWindow, isHandlingFileOpen and fileOpenSuppressionCount
when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@TablePro/AppDelegate.swift`:
- Around line 494-512: The handleSQLiteFile function currently misses dedup
checks for queued URLs and for sessions that are still connecting, and it
compares raw paths; fix it by canonicalizing the URL before any comparisons
(e.g., resolveSymlinks and standardized path/case into a single filePath
variable) then check queuedSQLiteFileURLs for an existing canonicalized URL and
skip appending if present, and update the active session loop over
DatabaseManager.shared.activeSessions to consider both fully connected
(session.driver != nil) and connecting sessions (session.connection.type ==
.sqlite && session.connection.database == canonicalFilePath) so duplicates are
not created while connecting; if a matching session exists, call
bringConnectionWindowToFront(sessionId) and return.
- Around line 206-221: The welcome-window suppression is being scheduled
immediately after queuing files, which can expire before queued files are
actually opened; update the flow so suppression covers the entire
queued-processing lifetime by either (A) deferring the call to
scheduleWelcomeWindowSuppression() until files are actually opened (move the
call into the code path that runs when WindowOpener.shared.openWindow returns a
non-nil window or when handleSQLiteFile(_:) confirms an opened window), or (B)
have scheduleQueuedSQLiteFileProcessing() (the routine that polls for SwiftUI
windows) take ownership of the suppression lifecycle: increment
isHandlingFileOpen/fileOpenSuppressionCount when queuing starts and
decrement/schedule cleanup only when the queue is drained and all queued files
processed. Ensure you reference handleSQLiteFile(_:),
scheduleWelcomeWindowSuppression(), scheduleQueuedSQLiteFileProcessing(),
WindowOpener.shared.openWindow, isHandlingFileOpen and fileOpenSuppressionCount
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ba96429-ea79-4163-8a6a-4c5b5cf07155

📥 Commits

Reviewing files that changed from the base of the PR and between 4b75de4 and 789b292.

📒 Files selected for processing (1)
  • TablePro/AppDelegate.swift

- AppDelegate.swift: class body, stored properties, lifecycle (103 lines)
- AppDelegate+FileOpen.swift: URL dispatch, deeplinks, plugins (214 lines)
- AppDelegate+ConnectionHandler.swift: DB URL, SQLite, unified queue (356 lines)
- AppDelegate+WindowConfig.swift: window lifecycle, dock, styling (320 lines)

Unified queuedDatabaseURLs + queuedSQLiteFileURLs into single
QueuedURLEntry enum with one polling loop. Extracted shared
suppressWelcomeWindow() and openNewConnectionWindow() helpers.
Replaced guard-return with if-block in SQL file handler.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (9)
TablePro/AppDelegate+WindowConfig.swift (4)

14-14: Add explicit access control to the extension.

Per coding guidelines, always specify access control explicitly on extensions. This extension should declare its visibility.

♻️ Suggested fix
-extension AppDelegate {
+internal extension AppDelegate {

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+WindowConfig.swift at line 14, The extension
declaration "extension AppDelegate" lacks explicit access control; update the
extension to include the appropriate visibility keyword (for example "internal
extension AppDelegate" or "public extension AppDelegate" depending on the module
API) so the extension and its members have explicit access control as required
by the coding guidelines.

62-85: Consider extracting shared connection logic to reduce duplication.

connectFromDock(_:) and attemptAutoReconnect(connectionId:) share nearly identical connect-then-handle-windows patterns: post .openMainWindow, call connectToSession, close welcome windows on success, close main windows and open welcome on failure. Consider extracting a shared helper.

♻️ Example extraction
private func performConnection(
    _ connection: DatabaseConnection,
    context: String
) {
    NotificationCenter.default.post(name: .openMainWindow, object: connection.id)
    
    Task { `@MainActor` in
        do {
            try await DatabaseManager.shared.connectToSession(connection)
            for window in NSApp.windows where self.isWelcomeWindow(window) {
                window.close()
            }
        } catch {
            windowLogger.error("\(context) failed for '\(connection.name)': \(error.localizedDescription)")
            for window in NSApp.windows where self.isMainWindow(window) {
                window.close()
            }
            self.openWelcomeWindow()
        }
    }
}

Also applies to: 279-311

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+WindowConfig.swift around lines 62 - 85, Both
connectFromDock(_:) and attemptAutoReconnect(connectionId:) duplicate the same
connect-and-window-handling flow; extract that logic into a single helper (e.g.,
performConnection(_ connection: DatabaseConnection, context: String)) that posts
.openMainWindow, runs Task { `@MainActor` ... } to call
DatabaseManager.shared.connectToSession(connection), closes welcome windows on
success (use isWelcomeWindow), and on failure logs with windowLogger including
the context and connection.name, closes main windows (isMainWindow), and calls
openWelcomeWindow; then replace the bodies of connectFromDock and
attemptAutoReconnect to call this helper with an appropriate context string.

288-310: Redundant DispatchQueue.main.async wrapping Task { @mainactor }.

Since attemptAutoReconnect is called from applicationDidFinishLaunching (already on main actor), and the inner Task { @mainactor } ensures main actor execution, the outer DispatchQueue.main.async is redundant.

♻️ Simplified version
-        DispatchQueue.main.async { [weak self] in
-            guard let self else { return }
-
-            NotificationCenter.default.post(name: .openMainWindow, object: connection.id)
+        NotificationCenter.default.post(name: .openMainWindow, object: connection.id)

-            Task { `@MainActor` in
-                do {
-                    try await DatabaseManager.shared.connectToSession(connection)
+        Task { `@MainActor` [weak self] in
+            guard let self else { return }
+            do {
+                try await DatabaseManager.shared.connectToSession(connection)

-                    for window in NSApp.windows where self.isWelcomeWindow(window) {
-                        window.close()
-                    }
-                } catch {
-                    windowLogger.error("Auto-reconnect failed for '\(connection.name)': \(error.localizedDescription)")
+                for window in NSApp.windows where self.isWelcomeWindow(window) {
+                    window.close()
+                }
+            } catch {
+                windowLogger.error("Auto-reconnect failed for '\(connection.name)': \(error.localizedDescription)")

-                    for window in NSApp.windows where self.isMainWindow(window) {
-                        window.close()
-                    }
+                for window in NSApp.windows where self.isMainWindow(window) {
+                    window.close()
+                }

-                    self.openWelcomeWindow()
-                }
+                self.openWelcomeWindow()
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+WindowConfig.swift around lines 288 - 310, Remove the
outer DispatchQueue.main.async wrapper and move the weak-self capture into the
Task to avoid redundant main-queue dispatching; replace the
DispatchQueue.main.async { [weak self] in guard let self else { return } Task {
`@MainActor` in ... } } block with a single Task { `@MainActor` [weak self] in guard
let self else { return } NotificationCenter.default.post(name: .openMainWindow,
object: connection.id) try await
DatabaseManager.shared.connectToSession(connection) ... } keeping the existing
error handling that calls windowLogger.error, closes windows via
isWelcomeWindow/isMainWindow, and calls self.openWelcomeWindow().

100-112: Add explicit access control to window identification methods.

These methods lack explicit access control. Per guidelines, specify internal or public explicitly.

♻️ Suggested fix
-    func isMainWindow(_ window: NSWindow) -> Bool {
+    internal func isMainWindow(_ window: NSWindow) -> Bool {
         guard let identifier = window.identifier?.rawValue else { return false }
         return identifier.contains("main")
     }

-    func isWelcomeWindow(_ window: NSWindow) -> Bool {
+    internal func isWelcomeWindow(_ window: NSWindow) -> Bool {
         window.identifier?.rawValue == "welcome" ||
             window.title.lowercased().contains("welcome")
     }

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+WindowConfig.swift around lines 100 - 112, The three
window-identification methods lack explicit access control; update the extension
and each member (isMainWindow(_:), isWelcomeWindow(_:) and currently-private
isConnectionFormWindow(_:)) to include explicit access levels per
guidelines—declare the extension with the appropriate access (likely internal)
and add explicit access modifiers on each function (e.g., internal func
isMainWindow(_:), internal func isWelcomeWindow(_:), private/internal func
isConnectionFormWindow(_:)) to match intended visibility.
TablePro/AppDelegate+FileOpen.swift (1)

14-14: Add explicit access control to the extension.

♻️ Suggested fix
-extension AppDelegate {
+internal extension AppDelegate {

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+FileOpen.swift at line 14, The AppDelegate extension
declaration lacks explicit access control; update the extension declaration for
AppDelegate in TablePro/AppDelegate+FileOpen.swift to include the appropriate
access modifier (e.g., public, internal, or private) consistent with the
surrounding API surface and coding guidelines, and ensure any members inside the
extension (methods/properties) retain or specify explicit access levels as
needed (e.g., make the extension "internal extension AppDelegate" or "public
extension AppDelegate" and adjust member modifiers accordingly).
TablePro/AppDelegate+ConnectionHandler.swift (2)

20-20: Add explicit access control to the extension.

♻️ Suggested fix
-extension AppDelegate {
+internal extension AppDelegate {

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+ConnectionHandler.swift at line 20, The AppDelegate
extension declaration lacks explicit access control; update the extension
declaration "extension AppDelegate" to include the appropriate access level
(e.g., public, internal, or private) consistent with the module's API rules and
the visibility of its members, and ensure any member declarations inside the
extension also have explicit access control modifiers if they differ from the
extension's level.

15-18: Add explicit access control to the enum.

♻️ Suggested fix
 /// Typed queue entry for URLs waiting on the SwiftUI window system.
 /// Replaces the separate `queuedDatabaseURLs` and `queuedSQLiteFileURLs` arrays.
-enum QueuedURLEntry {
+internal enum QueuedURLEntry {
     case databaseURL(URL)
     case sqliteFile(URL)
 }

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate`+ConnectionHandler.swift around lines 15 - 18, The enum
declaration for QueuedURLEntry lacks an explicit access modifier; add the
appropriate access control keyword (e.g., private, fileprivate, internal, or
public) before the enum name — e.g., "private enum QueuedURLEntry" — to make its
visibility explicit, and ensure any related members or extensions referencing
QueuedURLEntry likewise have explicit access control; the enum cases databaseURL
and sqliteFile will inherit that access level.
TablePro/AppDelegate.swift (2)

100-102: Minor inconsistency between comment and deinit.

Lines 74-76 state observers aren't explicitly removed because AppDelegate lives for the app lifetime, yet deinit removes observers. While defensive cleanup is fine, consider updating the comment to reflect this behavior.

♻️ Suggested comment update
         // NOTE: These observers are not explicitly removed because AppDelegate
-        // lives for the entire app lifetime. NotificationCenter uses weak
-        // references for selector-based observers on macOS 10.11+.
+        // lives for the entire app lifetime (deinit cleanup is defensive).
+        // NotificationCenter uses weak references for selector-based observers
+        // on macOS 10.11+.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate.swift` around lines 100 - 102, The comment claiming
observers aren't explicitly removed because AppDelegate lives for the app
lifetime is inconsistent with the presence of nonisolated deinit that calls
NotificationCenter.default.removeObserver(self); update the comment near
AppDelegate (and/or deinit) to state that although AppDelegate normally lives
for the app lifetime, there is a defensive cleanup implemented in deinit which
removes observers using NotificationCenter.default.removeObserver(self), so the
comment accurately reflects the code's behavior.

29-42: Add explicit access control to properties.

These properties lack explicit access control. While implicitly internal, the guidelines require explicit specification. Since they're accessed by extension files, internal is appropriate.

♻️ Suggested fix
     /// Track windows that have been configured to avoid re-applying styles
-    var configuredWindows = Set<ObjectIdentifier>()
+    internal var configuredWindows = Set<ObjectIdentifier>()

     /// SQL files queued until a database connection is active (drained on .databaseDidConnect)
-    var queuedFileURLs: [URL] = []
+    internal var queuedFileURLs: [URL] = []

     /// Database URL and SQLite file entries queued until the SwiftUI window system is ready
-    var queuedURLEntries: [QueuedURLEntry] = []
+    internal var queuedURLEntries: [QueuedURLEntry] = []

     /// True while handling a file-open event — suppresses welcome window
-    var isHandlingFileOpen = false
+    internal var isHandlingFileOpen = false

     /// Counter for outstanding suppressions; welcome window is suppressed while > 0
-    var fileOpenSuppressionCount = 0
+    internal var fileOpenSuppressionCount = 0

As per coding guidelines: "Always specify access control explicitly (private, internal, public) on both extensions and individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/AppDelegate.swift` around lines 29 - 42, Add explicit access control
to the listed AppDelegate properties by marking them as internal (the guideline
requires explicit access control for members accessed by extensions). Update the
declarations for configuredWindows, queuedFileURLs, queuedURLEntries,
isHandlingFileOpen, and fileOpenSuppressionCount to include the internal
modifier so their signatures read like "internal var configuredWindows = ...",
etc., leaving types and initial values unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@TablePro/AppDelegate.swift`:
- Around line 100-102: The comment claiming observers aren't explicitly removed
because AppDelegate lives for the app lifetime is inconsistent with the presence
of nonisolated deinit that calls
NotificationCenter.default.removeObserver(self); update the comment near
AppDelegate (and/or deinit) to state that although AppDelegate normally lives
for the app lifetime, there is a defensive cleanup implemented in deinit which
removes observers using NotificationCenter.default.removeObserver(self), so the
comment accurately reflects the code's behavior.
- Around line 29-42: Add explicit access control to the listed AppDelegate
properties by marking them as internal (the guideline requires explicit access
control for members accessed by extensions). Update the declarations for
configuredWindows, queuedFileURLs, queuedURLEntries, isHandlingFileOpen, and
fileOpenSuppressionCount to include the internal modifier so their signatures
read like "internal var configuredWindows = ...", etc., leaving types and
initial values unchanged.

In `@TablePro/AppDelegate`+ConnectionHandler.swift:
- Line 20: The AppDelegate extension declaration lacks explicit access control;
update the extension declaration "extension AppDelegate" to include the
appropriate access level (e.g., public, internal, or private) consistent with
the module's API rules and the visibility of its members, and ensure any member
declarations inside the extension also have explicit access control modifiers if
they differ from the extension's level.
- Around line 15-18: The enum declaration for QueuedURLEntry lacks an explicit
access modifier; add the appropriate access control keyword (e.g., private,
fileprivate, internal, or public) before the enum name — e.g., "private enum
QueuedURLEntry" — to make its visibility explicit, and ensure any related
members or extensions referencing QueuedURLEntry likewise have explicit access
control; the enum cases databaseURL and sqliteFile will inherit that access
level.

In `@TablePro/AppDelegate`+FileOpen.swift:
- Line 14: The AppDelegate extension declaration lacks explicit access control;
update the extension declaration for AppDelegate in
TablePro/AppDelegate+FileOpen.swift to include the appropriate access modifier
(e.g., public, internal, or private) consistent with the surrounding API surface
and coding guidelines, and ensure any members inside the extension
(methods/properties) retain or specify explicit access levels as needed (e.g.,
make the extension "internal extension AppDelegate" or "public extension
AppDelegate" and adjust member modifiers accordingly).

In `@TablePro/AppDelegate`+WindowConfig.swift:
- Line 14: The extension declaration "extension AppDelegate" lacks explicit
access control; update the extension to include the appropriate visibility
keyword (for example "internal extension AppDelegate" or "public extension
AppDelegate" depending on the module API) so the extension and its members have
explicit access control as required by the coding guidelines.
- Around line 62-85: Both connectFromDock(_:) and
attemptAutoReconnect(connectionId:) duplicate the same
connect-and-window-handling flow; extract that logic into a single helper (e.g.,
performConnection(_ connection: DatabaseConnection, context: String)) that posts
.openMainWindow, runs Task { `@MainActor` ... } to call
DatabaseManager.shared.connectToSession(connection), closes welcome windows on
success (use isWelcomeWindow), and on failure logs with windowLogger including
the context and connection.name, closes main windows (isMainWindow), and calls
openWelcomeWindow; then replace the bodies of connectFromDock and
attemptAutoReconnect to call this helper with an appropriate context string.
- Around line 288-310: Remove the outer DispatchQueue.main.async wrapper and
move the weak-self capture into the Task to avoid redundant main-queue
dispatching; replace the DispatchQueue.main.async { [weak self] in guard let
self else { return } Task { `@MainActor` in ... } } block with a single Task {
`@MainActor` [weak self] in guard let self else { return }
NotificationCenter.default.post(name: .openMainWindow, object: connection.id)
try await DatabaseManager.shared.connectToSession(connection) ... } keeping the
existing error handling that calls windowLogger.error, closes windows via
isWelcomeWindow/isMainWindow, and calls self.openWelcomeWindow().
- Around line 100-112: The three window-identification methods lack explicit
access control; update the extension and each member (isMainWindow(_:),
isWelcomeWindow(_:) and currently-private isConnectionFormWindow(_:)) to include
explicit access levels per guidelines—declare the extension with the appropriate
access (likely internal) and add explicit access modifiers on each function
(e.g., internal func isMainWindow(_:), internal func isWelcomeWindow(_:),
private/internal func isConnectionFormWindow(_:)) to match intended visibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11c4787c-8ca1-488a-aa75-09d2a413469a

📥 Commits

Reviewing files that changed from the base of the PR and between 789b292 and 5b30cc6.

📒 Files selected for processing (4)
  • TablePro/AppDelegate+ConnectionHandler.swift
  • TablePro/AppDelegate+FileOpen.swift
  • TablePro/AppDelegate+WindowConfig.swift
  • TablePro/AppDelegate.swift

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Double click on a .sqlite file to open it in TablePro

1 participant