最後までネイティブで、テキストが必要になるまでは
(justsitandgrin.im)- macOSでSwiftUIだけを使ってMarkdownチャットUIを作ると基本的な性能はある程度出せるが、文書全体の選択をサポートしにくい
NSTextViewとTextKit 2に移行すると、SwiftUIで積み上げたテストや性能改善を失い、ストリーミング入力でCPUスパイクが発生するNSCollectionViewでの再実装ではセルのちらつきが起き、純粋なTextKit 2でも性能は悪くないがストリーミング連携が良くない- WebKitはMarkdownレンダリング、性能、タイポグラフィ、制御性が全体的によく合っており、Electronでもテキスト処理が標準でうまく動く
- 長いチャットとリッチテキストではSwiftUIとAppleネイティブSDKが制約となり、Webベースのほうがテキスト・レンダリングモデルで有利になる
ネイティブmacOS MarkdownチャットUIの限界
- SwiftUIだけでMarkdown対応のシンプルなチャットを作ると基本性能はある程度確保できるが、SwiftUIプリミティブで構成されたMarkdown文書全体を選択できない
NSTextViewに移行するとTextKit 2対応は得られるが、SwiftUIで行ってきたテストや性能改善の大半を失い、SwiftUIとの相性も悪くなる- モデルの応答をストリーミング方式で
NSTextViewに流し込むとCPUスパイクが発生する NSCollectionViewで作り直してもセルが絶えずちらつき、設計上避けにくい挙動として残る- 純粋なTextKit 2プロトタイプは性能自体は悪くないが、ストリーミングは依然として弱く、モダンな構成要素とも相性がよくない
- SwiftUIを完全に外してAppKitだけを使っても、伸び続けるテキスト断片を手動で扱う必要があり、多くの部分が壊れた状態になってようやくテキスト選択が可能になる
- macOS標準の挙動に近い水準に達するには、コンテキストメニュー、辞書参照、選択、アクセシビリティ、テキストインタラクションのように、ユーザーが当然期待する機能を改めて揃えなければならない
WebKitとElectronがより適している点
- WebKitでMarkdownをレンダリングするといくつか注意点はあるものの、全体としてよく動作し、性能とタイポグラフィが良く、制御性も十分にある
- シンプルなElectronプロジェクトを作ると、テキスト処理、Markdownレンダリング、良好なタイポグラフィが最初から機能し、純粋なTextKit 2実装では得にくかった性能も出せる
- ElectronでもmacOS統合は提供されており、数行のコードで派手なGit diffをレンダリングでき、diffs.comのような事例もある
- SwiftUI、AppKit、TextKit、WebKitまで検討しても、「Markdownをサポートし、メッセージ全体を選択できるチャット」というシンプルな要件をきちんと満たすのは難しい
- チャット、長文のリッチテキスト、柔軟なタイポグラフィが重要な新しいアプリがWebベースへ向かう理由が、よりはっきりする
- SwiftUIはスクロールがあまり多くないシンプルな画面に適しており、Swiftは性能が重要な部分では今でも有用だ
- ElectronやReact Nativeはネイティブ相互運用によってかなりの性能を得つつ、より良いテキスト・レンダリングモデルを維持できる
- 長文チャット向けのリッチテキストレンダリングでは、SwiftUIとAppleネイティブSDKは強みではなく制約に変わる
- 関連議論: Hacker News, Lobsters
2件のコメント
Hacker Newsの意見
最近、TextKit 2を使ったiOS向けテキストエディタを公開したが、5,000行のファイルでも高い性能が出ている
Project GutenbergのMoby Dickでテストし、2025年8月から2026年4月の間に作成、開発は継続中とのこと
キー入力ごとに8ms以内で再スタイリングされ、デバウンスや遅延レンダリングなしでも高速な20回の入力を、各入力後の全文再スタイリング込みで150msで処理する
タグとブール検索は20ms以内で終わり、表示範囲だけをレンダリングすれば文書全体のスタイリングより25倍速く、120Hzの画面更新にも対応する
アプリのファイルサイズは1.0で722KB、機能が増えた1.1でも約950KBらしい
iOSでこれだけできるなら、macOSでは10倍くらい簡単であるべきだ
https://www.gingerbeardman.com/apps/papertrail/
「不可能だ」と言いたいのではなく、なぜ人々がこういう場面でネイティブではなくWeb技術を選ぶのか理解できる、という意味だ。作りたいのは製品であって、システムの限界と戦いたいわけではない
テキストビューアなら少なくともそれより2桁大きいファイルを扱えるべきだ。数十万行のJSONファイルは珍しくなく、CSVやログファイルはそれ以上に長い
以前はWebViewではなくネイティブAPIを使う理由は性能だったが、今は必ずしもそうではなさそうだ
ブラウザのレンダリングエンジンはかなり成熟していて、GPUアクセラレーションも多く取り入れられ、10年以上にわたって肥大化したWebアプリでストレステストされてきた
一方でSwiftUIは特に高速には感じない。Appleが最新向けに作り直したシステム設定も、UIをチェックボックス中心の行に単純化したのに、セクション切り替えがus-east-1からWebページを読み込むよりももたつくことがある
Qt C++とQMLでネイティブアプリを作り、同等のWebアプリよりずっと速く、RAMもはるかに少なくて済むことを示してきた
だから一般論として、Webアプリは適切に設計されたネイティブアプリより遅く、より多くのリソースを使う
[1] https://notes.alinpanaitiu.com/SwiftUI%20is%20convenient,%20...
[2] https://x.com/daniel_nguyenx/status/1734495508746702936
[3] https://rubymamistvalove.com/block-editor#8-performance
元々はWeb開発者だったが、ここ6〜12か月でネイティブのクロスプラットフォームアプリを作り始めて、単純な作業でも性能差がかなり大きいと感じている
数千人年分の最適化と、実環境での数百万人年分の検証を捨てて、もっと劣るテキストレンダリングエンジンを作り直そうと考える方がおかしい
macOSではWebKitはネイティブOSフレームワークだ。MarkdownレンダリングにWebKitを使うのは完全に妥当に思える
もちろん何もかもWebKitでレンダリングするのは、何もかもPDFKitでレンダリングするのと同じくらい筋が悪い。だがMarkdownビューならWebKitは論理的な選択であり、だからといって全面的にChromiumのWebアプリへ行く必要はない
それにOS Xは長い間、DisplayPDF/QuartzでUIをレンダリングしていた
WebKitは他のプラットフォームにもあるので反則だというのか? それならJavaを使ってもいいことになる
HTML/CSS/JSレンダラでテキストを描画するのが「完全に妥当」なら、妥当でない領域は何なのか? なぜ全部それで描画しないのか?
「テキストレンダリングには妥当だが、すべてのレンダリングには馬鹿げている」につながる論理が理解できない
macOSでWebKitがネイティブOSフレームワークだという点には同意するし、その意味では「ネイティブ」だ
しかし同時に、リッチテキスト、Markdown、選択、タイポグラフィ、長文の整形済みコンテンツを正しく扱うには、Web技術が急速に唯一の実用的な選択肢になっているという大きな論点も裏づけている
MarkdownビューにWebKitを使うのが間違いだと言いたいのではなく、むしろ最も合理的な選択である可能性が高い。問題は、ここでの「ネイティブ」解法が実質的にはWebレンダリング解法だという点だ
各
WKWebViewは独自の性能およびメモリオーバーヘッドを持つWebKitエンジンを抱えるので、どこにでもばらまいて無料のネイティブmacOSコンポーネントのように扱えるわけではないこの種のUIにおいて、SwiftUI / AppKit / TextKitが「とにかくWebKitを使え」よりも優れた、クリーンでモダンかつ合成可能な道筋を提供してくれないのがもどかしい
「Markdown付きチャットでメッセージ全体を選択できるようにする」程度のことができないというのは、どうにもおかしい気がする
SwiftUIには成熟したMarkdownレンダラを活用できる。 https://github.com/gonzalezreal/swift-markdown-ui と、その次世代の代替である https://github.com/gonzalezreal/textual を見ればいい
実際に使ったことがあるが問題はなかった。SwiftやSwiftUIが好きではなくObjective-Cを好むような自分のような変人でも、LLMの助けなしにやれた
静的な完成済みMarkdownのスクロールは新しいフォーカスプローブを通過できず、p95は18.86msで16.7msの予算を超え、最大は232.49msだった
長いリアルタイムMarkdown/コード更新の経路も失敗で、p95は59.33ms対16.7ms、最大75.94msだった。更新中に大きなリッチテキスト面を扱う、別ではあるが関連したストレスケースだ
長い履歴の拡張は技術的には通るが、滑らかなフレームとは言い難い: 120ターンでp95 21.35ms、500ターンで23.11ms、1000ターンで36.77ms
悪くはないが、自分の解法よりわずかに遅く、主にTextualの実装というよりSwiftUIに関係する似たような性能差がある
以前アプリでswift-markdown-uiを使ったが、性能はwkwebviewにまったく及ばなかった。大きな表、コードブロック、ネストした引用のような厄介な要素がある大きな文書をストリーミングすると、レインボーカーソルまで見ることがあったが、wkwebviewを使うとそういうことはなかった
そのうち、ブラウザとその基盤技術がUIに新しいパラダイムを持ち込み、ネイティブUIフレームワークがそれに追いつけなかったのだと気づいた
Webベースアプリよりネイティブアプリを好む立場でも、そう感じる
コードを見せるか、さもなくば退場だ。今でもMarkdownレンダリングとストリーミングテキストをきちんと処理するネイティブなMac/iOSアプリは非常に多い
いったいどんな言い訳があるのか知りたいだけだ
たいていは連続する各ブロック内だけ選択をサポートし、メッセージ全体にはコピーボタンを置く形に落ち着いている
面白いことに、Appleも以前はこうしていた
昔のmacOS / AppKitは、ネイティブなNSTextField内のリッチテキストをレンダリングするのにWebKitを使っていた。テキストは難しい問題だ
そのうえネイティブWebViewは非常に高速で軽量なので、テキストレイアウトエンジンとして使うのも不自然ではない。テーブルの各行ごとに別々のWebViewを使っても優れた性能が出ることがある
Mac向けiMessageもWebViewを使っていたし、Adiumもそうだった。リッチ/マークアップテキストをレンダリングするなら、HTMLは完全に適した道具だ
MacはNSTextFieldのレンダリングにWebKitを使ったことはない。iOSが最初に作られたときは、UIKitコントロールを含めて全体的にWebKitをテキストレンダラとして使っていて、それを「sweet solution」と呼んでいた
しかし重すぎて扱いづらいことがわかり、Core Text/AppKit式のテキストレンダリング手法へ移行した
複雑なネイティブテキストレンダリングが難しいと気づき、低レベル方式でテキストをレンダリングしながら、ネイティブな相互作用を再実装しなければならないと不満を言う
WebKitを試したら非常にうまくいったのに、それを捨てて再びネイティブな相互作用を実装しなければならない状況へ戻っている
個人的にはWebKitがうまく動く時点で止めていただろう
2015年にジュニアエンジニアだった頃、iOSアプリの段落内にクリック可能なリンクをレンダリングしろという仕事を任された記憶がある
Swiftが出たばかりの時期で、完全にObjC/UIKitスタックだったが、本当に悪夢のようだった。何とか動くようにはした
2016年ごろ以降iOSはほとんど触っていないので、新しいSwiftUIには当然こういうものが標準で入っていると思っていたが、そうではなかったとはかなり狂っている
https://developer.apple.com/documentation/swiftui/link
この時点で、これ以上どう簡単にできるのかわからない
「単純な画面を超えるとネイティブはまだこんなに未成熟だ」というのは当然のことだ
人々が十分な努力を注がなければ、それが成熟することを期待できない
より多くの努力がWeb技術に集まるため、人々はそこに縛られている。ネイティブを見て「十分に発展していない」と言ったあと、またWeb開発を増やすという循環が繰り返される
ブラウザではすでに「ただ動く」ので、ネイティブを改善しようと努力する人はほとんどいない
Web側がはるかに成熟している理由のひとつは、大手商用OSメーカーが時代について行こうとしなかったからだ。WindowsのUIキットは本当にひどい
自分のAIチャットアプリでもほぼ同じ経験をした。まともに動くものがない
Markdownレンダリングは遅くてもたつき、ストリーミングも遅くてもたつき、すべてがUIを固まらせる
GitHubで人気のあるUIKitおよびSwiftUI向けテキスト編集コンポーネントを少なくとも5つは試したが、全部どこかしら壊れているかバグがあり、しかも遅かった。信じられない
これは難しい問題だ。Qt C++とQMLでブロックエディタをゼロから作り、どう解決したかを長く書いた
不連続なブロック間の選択、カーソル下の元Markdown表示、異なるdelegateサイズなど、似たような問題に直面した
そのとき学んだことをもとに、ストリーミングMarkdownパーサを備えたネイティブLLMクライアントを作っている
[1] https://rubymamistvalove.com/block-editor
[2] https://www.get-vox.com
Lobste.rsの意見
Electronは実質的にWebViewを包んだラッパーだが、Chromiumエンジン全体を一緒に抱え込むため、利便性の代償としてアプリサイズが大きくなりすぎる
2001〜2002年にiChatの象徴的な吹き出しテキストレイアウトをNSTextViewとあらゆる裏技で実装したことがあるが、AppKitのテキスト設計者だったHideki Itamuraの助けを借りてもかなり苦労した。今ならHTML+CSSでかなり簡単になっている
Tauriを使っていたアプリに関わったが、ElectronでChromiumを使うように変えたら、はるかによく動いた。特にwin7からwin11まで非常に広い範囲を対象にしていた点も重要だった
Tauri系はシステムWebViewを使う点が良さそうに見えるが、WindowsではChromeやEdge、macOSではSafari、それ以外の環境では運任せになる。結局、予測可能性と成熟度が勝ち、理由は分からないが性能もより良かった
結局、
htopのグラフを満足させることより、より良いソフトウェアを望んでいるブログ記事の要点は「みんながElectronを選ぶべきだ」ではなく、リソースと資金のある企業でさえ、なぜ今でもElectronやWeb技術を選ぶのかを理解した、ということだ。少なくとも自分の基準では、適切なところで妥協しつつ良いユーザー体験を提供している
AppleのTextKit 2や他のネイティブなテキストフレームワークを擁護する人は多いが、Apple SDKだけで作られた人気があり高性能なテキストエディタはほとんどない。Xcodeはかなり健闘しており、おそらく唯一の実運用例に近い。Zed、Sublime Text、Visual Studio Code、JetBrains IDEはいずれも理由があって独自のテキストレンダリングソリューションを使っている
そのため、最悪のやり方で作っても自分の環境では問題なさそうに見えることがある
一方で、はるかに低スペックな機器を使う大多数の人たちは、肥大化して遅いソフトウェアを押し付けられ続ける
ただし筆者はここでその方面までは探っていないようだ
外から見ると、Flutterは「ChromiumからレンダラーのSkiaだけを残してGUIアプリに使ったらどうなるか?」への答えのように見える。Electronより軽量でありながら、似た機能を持つクロスプラットフォームツールであるべきに思える
contentEditableDIV一つを使うHTML文書とやり取りし、WKWebViewのサブクラスを使用すると書かれている皮肉なのは、Appleのネイティブなテキストエンジンが、HTML DOMツリーよりはるかに優れたデータモデル、つまり文字列とランレングス符号化されたスタイル属性を使っていることだ
DOMでテキストを編集するのは悪夢に近く、選択範囲が複数階層の複数要素をまたぐことが多いため、非常に複雑に分割と結合をしなければならないからだ。昔、
contenteditable対応が入ったSafariビルドを初めて触ったときは大量のバグレポートを送ったし、今でも多くのWebリッチテキストエディタはリスト項目を切り取ったり貼り付けたりすると壊れる結局、90〜00年代のCISC対RISCに似たことが起きたように思える。「劣った」構造により多くのリソースが投入され、結果としてより優れた実装が生まれたわけだ