A veteran macOS/iOS developer recounts the frustrating path of building a Markdown‑enabled chat UI with native Apple frameworks, only to discover that web‑based stacks like Electron deliver smoother text handling and richer typography out of the box.
The promise of “native all the way”
For almost two decades I have built macOS and iOS apps using the tools Apple provides—Objective‑C, Cocoa, Swift, and more recently SwiftUI. The mantra in many circles is that staying native guarantees the best performance, the smoothest integration, and the most "Apple‑like" experience. When a new project required a simple chat interface with Markdown support, I started with the most native stack I could think of: pure SwiftUI.
First attempt: SwiftUI primitives
SwiftUI can render text, images, and even basic markdown. Scrolling feels acceptable on short screens, and the declarative model makes UI code tidy. However, as soon as the chat grew beyond a handful of messages, two problems surfaced:
- Selection – SwiftUI’s
Textview does not expose a way to select an entire markdown document. The framework deliberately treats text as static content. - Performance hiccups – When I tried to stream model‑generated responses (the 2026 norm), the UI began to stutter, and the CPU usage spiked.
I could shrug off a few dropped frames on a prototype, but a production‑grade chat needs reliable, buttery scrolling and selectable content.
Switching gears: NSTextView and TextKit 2
The next logical step was to drop down to AppKit and embed an NSTextView. The view now runs on top of TextKit 2, Apple’s modern text layout engine. It brings powerful features:
- Rich text attributes
- Native spell‑checking and dictionary lookup
- Accessibility support out of the box
Unfortunately, integrating NSTextView into a SwiftUI hierarchy proved messy. The view does not cooperate with SwiftUI’s state‑driven updates, so I lost the testing harness and performance optimisations I had built around the SwiftUI version. Streaming text into the view still caused noticeable CPU spikes, and any attempt to animate updates resulted in flickering.
Going deeper: NSCollectionView and raw TextKit 2
I then tried the classic NSCollectionView route, treating each chat bubble as a collection cell. The framework is battle‑tested and performant for large data sets, but it has a built‑in limitation: cells are redrawn on every data change, which caused a persistent blink effect when new messages arrived. The blink is not a bug; it is how the view reconciles layout changes.
A more radical experiment involved abandoning the higher‑level views entirely and building a custom renderer with TextKit 2. The prototype handled static markdown reasonably well, but streaming updates still lagged, and the code quickly became a maintenance nightmare. I was now writing low‑level layout logic that Apple’s higher‑level APIs were supposed to abstract away.
The “just work” moment: WebKit
At this point I considered embedding a WebKit view to render the markdown. The result was surprisingly pleasant:
- Performance – The Chromium‑based engine handled large blocks of text with minimal CPU overhead.
- Typography – CSS gave me fine‑grained control over fonts, line‑height, and code‑block styling.
- Feature parity – Context menus, dictionary lookup, and accessibility were already baked in.
The downside was the need to maintain a small HTML/CSS layer and bridge communication between Swift and JavaScript. Still, it felt like a step forward compared to the native dead‑ends.
The dark side beckons: Electron
Curiosity (and a dash of desperation) led me to spin up a minimal Electron project. The experience was eye‑opening:
- Text operations – Selecting, copying, and searching markdown content worked flawlessly, thanks to the browser’s mature text engine.
- Rendering speed – Even with continuous streaming updates, the UI stayed fluid, and CPU usage stayed under control.
- macOS integration – Electron’s native APIs (via the
electron-macmodule) allowed me to expose the same context‑menu actions and accessibility features I’d been fighting for in AppKit.
A few lines of JavaScript turned a clunky native prototype into a polished chat window. The performance gap that had plagued my SwiftUI/TextKit attempts vanished.
What does this tell us about the current state of native macOS UI?
Community sentiment
Many developers still champion “native all the way” because Apple’s frameworks feel familiar and the ecosystem promises long‑term stability. The sentiment is especially strong for performance‑critical components like video decoding or low‑level graphics.
Adoption signals
- SwiftUI adoption is rising for simple forms, dashboards, and onboarding screens.
- AppKit remains the go‑to for legacy, complex editors, and tooling where fine‑grained control is essential.
- Web‑based stacks (Electron, Tauri, React Native for macOS) are gaining traction in chat‑heavy, markdown‑rich applications.
Counter‑arguments
- Native APIs are evolving – Apple is actively improving TextKit 2 and SwiftUI’s scroll performance. Future releases may close the current gaps.
- Security and bundle size – Electron ships a full Chromium runtime, increasing attack surface and app size, which some users and reviewers frown upon.
- Platform consistency – Pure native apps automatically inherit macOS look‑and‑feel updates (e.g., Dark Mode, system fonts) without extra work.
Where to go from here?
If your product’s core value lies in rich, scroll‑intensive text—think chat, collaborative documents, or markdown editors—consider a hybrid approach:
- Prototype with WebKit for rapid iteration and typography control.
- Evaluate Electron or Tauri if you need seamless streaming and selection across large data sets.
- Reserve SwiftUI/AppKit for parts of the app that truly benefit from native performance (e.g., media playback, hardware‑accelerated graphics).
The key takeaway is that “native all the way” is no longer a universal guarantee of success. Modern web rendering engines have matured to a point where they can out‑perform native text stacks for certain workloads, while still offering deep macOS integration.
If you’re curious about the tools mentioned, here are some quick links:

The journey from pure SwiftUI to Electron illustrates a broader pattern: developers are increasingly willing to cross the native‑web boundary when the latter offers a smoother user experience for complex text handling.

Comments
Please log in or register to join the discussion