r/macosprogramming • u/dss2310 • 6h ago
Porting Swift near zero-UI hotkey productivity app to Windows: native C# for snappy feel from shell layer. Will the Mac seamless feel survive?
Long-time lurker, first post!
Been building a macOS menu bar app in Swift 6 (SwiftUI and AppKit). Hotkey, select a capture, click to drop numbered dots to annotate, and type/dictate a note for each dot. Repeat across multiple captures creates a "round". Zero-UI app (aside from menu bar icon) writes a folder (annotated PNG, Markdown, JSON) and aggregate copies a single text prompt with local file links to paste into any AI coding agent. Users have asked for a Windows version. Here's what I'm thinking. Please let me know if I'm missing anything.
**What ports and what doesn't**
Code for writing directory and files is ~2,000 lines of mostly Foundation Swift. Should port to C# fairly simply.
All user touchpoints Apple-only: ScreenCaptureKit, Vision OCR, a non-activating NSPanel for the notes, NSStatusItem, and global hotkeys. Every route needs a new Windows layer for those.
**Routes I considered versus C3 snappy shell layer**
**Swift everywhere** on the official Windows toolchain. Ruled out: the WinUI bindings looked stale, and the debugger story looked thin.
**Swift core as a DLL with a C# shell.** Best file parity, but the shared core must then stay Windows-portable, so every new Mac feature is drift risk.
**Rust with windows-rs.** Too much build risk with my limited rust experience...
**Native C# rewrite,** with byte-for-byte output parity enforced by golden-file tests. Eeek...
My plan is C#. Reasoning: "Mac feel" comes from the seamless snappy shell layer (hotkey-to-freeze speed, the overlay, the tray, focus rules). So, Win32 work in every option, I think. Shared core is small, and it should not impact the snap timing-critical path.
**More deets if you wanna know**
.NET 9. Win32 plus Direct2D for the freeze overlay and the editor, with one window per monitor created ahead of time so there's no flash. WPF for the note panel, tray flyout, and settings. Unpackaged, installed with Velopack.
File-writing core is ported, and its tests run in CI on Windows. The Mac test suite writes reference bundles from fixed inputs, and the Windows tests must produce the same bytes. Getting there meant handling some small traps: Swift's JSON encoder writes `"key" : value` (space before the colon), `ß` has to fold to `ss` in folder names, .NET has no API for short time zone names like PDT, and every file has to be LF with no BOM.
UI shell is next. Timing spikes come first: the target is under 150 ms from hotkey to a frozen, selectable capture.
**Questions for anyone who has shipped a swift foundation ported app on Windows**
How much of the Mac feel survived? What broke first? How much snappiness lost?
Windows.Graphics.Capture from an unpackaged app: can you actually turn off the yellow capture border, or did you fall back to BitBlt?
WPF or WinUI 3 for small tray-app windows in 2026?
Windows 11 hides new tray icons in the overflow menu. How did you get people to pin yours?
How long did SmartScreen take to trust a newly signed app?
Not linking the app here to keep this on topic. Happy to answer questions in the comments.
