Skip to content

Fix notification click launching second app instance#30

Merged
trydis merged 1 commit intomainfrom
fix/notification-click-second-window
Mar 17, 2026
Merged

Fix notification click launching second app instance#30
trydis merged 1 commit intomainfrom
fix/notification-click-second-window

Conversation

@trydis
Copy link
Owner

@trydis trydis commented Mar 17, 2026

  • Add single-instance guard in ShellraiserApp.init() that activates the existing instance and exits if another copy with the same bundle ID is already running — guards against Launch Services conflicts from builds across checkouts
  • Switch WindowGroup to Window (semantically correct for single-window)
  • Add applicationShouldHandleReopen to restore window on dock-click
  • Remove -n flag from Makefile run-isolated to prevent duplicate launches
  • Add tests for applicationShouldHandleReopen covering both hasVisibleWindows states

Summary by CodeRabbit

  • New Features

    • Clicking the dock icon now brings the main app window to the front, even when it's hidden from view.
    • The application now detects when it's already running and reuses the existing instance instead of launching multiple copies.
  • Bug Fixes

    • Enhanced window management and instance handling for improved reliability and user experience.

- Add single-instance guard in ShellraiserApp.init() that activates
  the existing instance and exits if another copy with the same bundle
  ID is already running — guards against Launch Services conflicts from
  builds across checkouts
- Switch WindowGroup to Window (semantically correct for single-window)
- Add applicationShouldHandleReopen to restore window on dock-click
- Remove -n flag from Makefile run-isolated to prevent duplicate launches
- Add tests for applicationShouldHandleReopen covering both
  hasVisibleWindows states
@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

The changes implement single-instance application behavior for Shellraiser. The app now detects existing instances with the same bundle ID, activates them, and exits. Dock icon clicks bring the main window to front. The main window is explicitly named and identified, and build configuration is updated to reuse existing app instances during development.

Changes

Cohort / File(s) Summary
Single Instance Application Lifecycle
Sources/Shellraiser/App/ShellraiserApp.swift, Tests/ShellraiserTests/ShellraiserAppTests.swift
Adds instance detection in app initialization to activate existing instances with matching bundle ID. Adds applicationShouldHandleReopen delegate method to bring main window to front on dock icon click. Includes two new tests verifying reopen handling returns true regardless of window visibility.
Window Management
Sources/Shellraiser/App/ShellraiserApp.swift
Replaces WindowGroup with a named Window("Shellraiser", id: "main") for the main app window while preserving content and size constraints.
Build & Launch Configuration
Makefile
Removes the -n flag from the open command in the run target for isolated app, allowing the system to reuse an existing application instance instead of forcing a new process.
🚥 Pre-merge checks | ✅ 3
✅ 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 directly describes the main problem being solved: preventing duplicate app instances when notifications are clicked, which is the core objective of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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
  • Commit unit tests in branch fix/notification-click-second-window
📝 Coding Plan
  • Generate coding plan for human review comments

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

Tip

You can customize the tone of the review comments and chat replies.

Configure the tone_instructions setting to customize the tone of the review comments and chat replies. For example, you can set the tone to Act like a strict teacher, Act like a pirate and more.

Copy link

@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 (1)
Sources/Shellraiser/App/ShellraiserApp.swift (1)

126-136: Single-instance guard implementation is functional but consider activation edge cases.

The logic correctly detects and activates an existing instance. A few observations:

  1. existing.activate() returns a Bool indicating success — currently ignored. If activation fails (e.g., the other instance is hung), the new process still exits, potentially leaving users without a responsive window.

  2. Using exit(0) in init() is unconventional but acceptable here since it's before any resources are allocated.

Consider optionally handling the activation failure case:

🔧 Optional: Handle activation failure
         if let existing = others.first {
-            existing.activate()
-            exit(0)
+            if existing.activate() {
+                exit(0)
+            }
+            // If activation failed, proceed to launch this instance
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Shellraiser/App/ShellraiserApp.swift` around lines 126 - 136, The
single-instance guard currently activates an existing app via
existing.activate() and unconditionally calls exit(0); change this so you check
the Bool result of existing.activate() and only call exit(0) when activation
succeeded; if activate() returns false (activation failed or the other instance
is hung) allow the initializer to continue launching (or implement a retry/alert
path) so the new process doesn't exit blindly. Locate the logic in init() where
NSRunningApplication.runningApplications(withBundleIdentifier:) is filtered,
inspect the existing.activate() call and replace the unconditional exit(0) with
a conditional exit based on the activation result (or alternative fallback
behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Sources/Shellraiser/App/ShellraiserApp.swift`:
- Around line 126-136: The single-instance guard currently activates an existing
app via existing.activate() and unconditionally calls exit(0); change this so
you check the Bool result of existing.activate() and only call exit(0) when
activation succeeded; if activate() returns false (activation failed or the
other instance is hung) allow the initializer to continue launching (or
implement a retry/alert path) so the new process doesn't exit blindly. Locate
the logic in init() where
NSRunningApplication.runningApplications(withBundleIdentifier:) is filtered,
inspect the existing.activate() call and replace the unconditional exit(0) with
a conditional exit based on the activation result (or alternative fallback
behavior).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd4de059-cd22-4016-b918-1cfd1513bf25

📥 Commits

Reviewing files that changed from the base of the PR and between f2c6bb5 and 68866ef.

📒 Files selected for processing (3)
  • Makefile
  • Sources/Shellraiser/App/ShellraiserApp.swift
  • Tests/ShellraiserTests/ShellraiserAppTests.swift

@trydis trydis merged commit d8d3536 into main Mar 17, 2026
2 checks passed
@trydis trydis deleted the fix/notification-click-second-window branch March 17, 2026 20:59
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.

1 participant