Native all the way, until you need text

Native all the way, until you need text

原生开发之路,直到你需要处理文本

I have been a native macOS / iOS developer for almost twenty years, and I want to say something about the usual “Oh, it is Node / Electron again… what a shame…” reaction. 作为一名拥有近二十年经验的 macOS/iOS 原生开发者,我想针对那种常见的“噢,又是 Node/Electron……真是太可惜了……”的反应谈谈我的看法。

Recently, I tried to implement a simple chat with Markdown support in a pure Swift / SwiftUI app. And honestly, it is almost funny how immature all these “native” things still are when you step outside simple screens. 最近,我尝试在一个纯 Swift/SwiftUI 应用中实现一个支持 Markdown 的简单聊天功能。老实说,当你跳出简单的界面范畴时,这些“原生”工具的成熟度之低简直令人发笑。

Yes, you can achieve reasonable performance in SwiftUI. You can even convince yourself that jumpy scrolling is fine, and that a few lags here & there are acceptable. But then you want to select a whole Markdown document built from SwiftUI primitives, and you just cannot. By design. 没错,你可以在 SwiftUI 中获得尚可的性能。你甚至可以自我安慰说,滚动时的跳动没关系,偶尔的卡顿也是可以接受的。但当你想要选中一段由 SwiftUI 原生组件构建的完整 Markdown 文档时,你却做不到。这是设计使然。

So, being smart & experienced, you move to NSTextView. It even supports TextKit 2 now. Great. Except now you lose most of the testing & performance work you had around SwiftUI, because it does not play well with it. 于是,凭借聪明才智和经验,你转向了 NSTextView。它现在甚至支持 TextKit 2 了,这很好。但问题在于,你之前围绕 SwiftUI 所做的测试和性能优化工作几乎全部作废,因为它们之间并不兼容。

Then you try to stream text into it, because it is 2026 & everyone streams responses from models now, and you start seeing CPU spikes. Fine. We still have AppKit. We still have NSCollectionView. Mature, performant, battle-tested. So you switch again, implement the whole thing, and on the second day you realise the cells will blink no matter what. By design. 接着你尝试向其中流式传输文本,毕竟现在是 2026 年,每个人都在从模型中流式获取响应,结果你开始看到 CPU 飙升。好吧,我们还有 AppKit,还有 NSCollectionView。它们成熟、高效且久经考验。于是你再次切换方案,重新实现了一遍,结果第二天你发现无论如何单元格都会闪烁。这也是设计使然。

Then you even consider going lower-level with pure TextKit 2. You make a prototype. Performance is okay. Streaming is still terrible. It does not play well with anything modern. You remove SwiftUI completely, stick with AppKit, and start fighting expanding text chunks manually. At this point almost everything is broken, but hey, you can select the text! 然后你甚至考虑使用纯 TextKit 2 进行底层开发。你做了一个原型,性能还行,但流式传输依然糟糕,且无法与任何现代技术兼容。你彻底移除了 SwiftUI,坚守 AppKit,开始手动处理文本块的扩展。此时几乎一切都坏掉了,但嘿,你终于可以选中文字了!

Then you realise it will take months just to reach feature parity with basic native macOS behaviour: context menus, dictionary lookup, selection, accessibility, text interactions, all the small things users expect without thinking about them. 接着你意识到,仅仅是为了达到 macOS 原生行为的功能对等,就需要花费数月时间:上下文菜单、词典查询、文本选择、辅助功能、文本交互,以及所有用户习以为常的细节。

So you try WebKit to render Markdown. And it works. There are caveats, of course, but mostly it just works. Performance is good. Typography is almost perfect. You have a proper level of control. 于是你尝试用 WebKit 来渲染 Markdown。它成功了。当然会有一些局限性,但总体上它确实好用。性能不错,排版近乎完美,你拥有了恰到好处的控制权。

And then, at the darkest possible moment, you think: okay, let’s generate a simple Electron project. You go to the dark side. And you are amazed. Text operations, Markdown rendering, good typography – all of it works out of the box, with performance you could not get even from your pure TextKit 2 implementation. macOS integrations are there too. You can even render fancy Git diffs with a few lines of code. I am not even talking about things like diffs.com. 就在最绝望的时刻,你心想:好吧,生成一个简单的 Electron 项目试试。你投向了“黑暗面”,结果令你大吃一惊。文本操作、Markdown 渲染、优秀的排版——所有功能开箱即用,性能甚至超过了你纯 TextKit 2 的实现。macOS 的集成功能也一应俱全。你甚至可以用几行代码渲染出精美的 Git diff。我甚至还没提像 diffs.com 那样的东西。

And then you ask yourself: what went wrong? I did everything people say you should do. Native all the way. I know the platform. I know the options. I know SwiftUI, AppKit, TextKit, WebKit. But I still cannot make a simple thing work properly: a chat with Markdown & the ability to select a whole message. 然后你问自己:到底哪里出了问题?我做了人们建议的一切。坚持原生开发。我了解这个平台,了解各种选项,精通 SwiftUI、AppKit、TextKit 和 WebKit。但我依然无法让一件简单的事情正常工作:一个支持 Markdown 且能选中整条消息的聊天功能。

And suddenly it becomes much clearer why most new chat-heavy apps that depend on one of the most important interface patterns of this era – chat, long-form rich text, flexible typography – are web-based in one way or another. There is no real alternative. 突然间,一切变得清晰起来:为什么大多数依赖于当今时代最重要的界面模式(聊天、长篇富文本、灵活排版)的新型聊天应用,多多少少都是基于 Web 的。因为根本没有真正的替代方案。

SwiftUI is fine for simple screens, preferably without too much scrolling. Swift is still great for performance-critical parts. But you can get most of that performance from Electron or React Native almost for free with the native interoperability, while keeping a much better text & rendering model. SwiftUI 适合简单的界面,最好不要有太多的滚动。Swift 在性能关键部分依然表现出色。但通过 Electron 或 React Native,你几乎可以免费获得大部分性能,同时还能利用原生互操作性,并保持更好的文本和渲染模型。

So this is not even a “quick solution vs proper solution” debate anymore. If you want to build rich text rendering for long-form chats, SwiftUI & Apple’s native SDKs are not helping you. They stop being an advantage & start becoming constraints. 所以,这已经不再是“快速方案与正统方案”的争论了。如果你想为长篇聊天构建富文本渲染,SwiftUI 和苹果的原生 SDK 帮不了你。它们不再是优势,反而成了束缚。