世界を動かす技術を、日本語で。

ネイティブな体験、テキストが必要になるまで

概要

  • macOS/iOSネイティブ開発者 として20年の経験を持つ筆者の体験談
  • SwiftUIやAppKit によるリッチテキスト・チャット実装の難しさ
  • WebKitやElectron の方が実用的な理由
  • ネイティブ開発の限界 と現代アプリの要件
  • Electron等の選択が合理的 である現状の指摘

ネイティブ開発者が感じるSwiftUI・AppKitの限界

  • Swift/SwiftUI でMarkdown対応チャットを実装しようとした体験談
  • シンプルな画面ではSwiftUIの パフォーマンスは十分、しかし複雑な処理では問題が顕在化
  • Markdownドキュメント全体の選択 がSwiftUIプリミティブでは不可能(設計上の制約)
  • NSTextView に切り替えるも、SwiftUIとの連携やテスト・パフォーマンス面での恩恵を失う
  • テキストストリーミング でCPUスパイク発生、パフォーマンス問題
  • AppKit/NSCollectionView に戻るも、セルのブリンク(ちらつき)が避けられない
  • TextKit 2 でプロトタイプ作成、パフォーマンスは許容範囲だがストリーミングは依然として非効率
  • SwiftUIを完全排除しAppKitに専念しても、 テキスト拡張処理 など手作業が増え、全体が壊れやすい
  • 基本的なmacOS機能(辞書検索、選択、アクセシビリティ等) の実装に膨大な工数が必要

WebKit・Electronの合理性と現代アプリの要件

  • WebKitでMarkdown描画 を試すと、パフォーマンス・タイポグラフィ・制御性が良好
  • Electron で簡単なプロジェクトを作成したところ、 テキスト操作やMarkdown描画が即動作
  • macOS統合 や高度なGit diff描画も数行で実現可能
  • TextKit 2の自作実装よりも高性能 で、必要な機能がほぼ「箱出し」で利用可能

なぜWebベースの選択が主流なのか

  • リッチテキスト・チャット や柔軟なタイポグラフィなど、現代アプリの主要UIパターンはWeb技術が得意
  • SwiftUIはシンプル画面やパフォーマンス重視部分には最適 だが、リッチテキストには不向き
  • ElectronやReact Native なら、ネイティブ連携を維持しつつ優れたテキストレンダリングが可能
  • もはや「クイックソリューション vs 本格ソリューション」の議論ではなく、 ネイティブSDKが制約となる時代
  • SwiftUIやApple純正SDK はリッチテキストチャットには現実的な選択肢ではない

結論

  • 現代のチャット・リッチテキストアプリ 開発にはWeb技術が不可欠
  • ネイティブ技術の強み が活かせる範囲は限定的
  • Electron等の選択は妥協ではなく合理的判断 となっている現状

Hackerたちの意見

自分のAIチャットアプリでもほぼ同じような経験をしたよ。何も上手くいかない。Markdownのレンダリングは遅いし、ラグもあるし、ストリーミングも遅くてラグがある。UIがロックアップすることも多い。GitHubでUIKitやSwiftUIの人気のテキストエディタコンポーネントを少なくとも5つ試してみたけど、どれも何かしら壊れてて、バグだらけで、遅かった。ほんと馬鹿げてる。

コード見せるか、さもなくば出て行ってもらうよ。今はMarkdownをレンダリングしたり、テキストをストリーミングしたりできるネイティブのMacやiOSアプリがたくさんあるから、こいつの言い訳が気になるね。

ウェブビューなし? コード見せてくれる?

OPは「SwiftUIのプリミティブから作られたMarkdownドキュメント全体を選択したい」と言ってるけど、誰がそんなことを望むの? どんな製品思考がそれを求めてるの?それってドキュメントエディタのように聞こえるけど、何十年も作るのが難しいものだし、LLMチャットUIには範囲外だよね。みんなは結局、連続したブロック内での選択だけをサポートして、全メッセージのコピー用ボタンを用意するって感じだよ。

2015年にジュニアエンジニアだった頃を思い出すな。iOSアプリ内で段落にクリック可能なリンクをレンダリングするように頼まれたんだ。Swiftがちょうどリリースされたばかりで、まだObjC/UIKitスタックしか使ってなかった。ほんと悪夢だった。なんとか動かすことができたけど、ギリギリだったよ。それ以降、2016年くらいからiOSにはほとんど触れてないから、新しいSwiftUIの機能にはこれが組み込まれてると思ってたんだけど、当然のことながらそうじゃなかった。ありえないよね。

Qtは10年前にこれをかなり簡単にしてくれたよ。

属性付きテキストはずっと前からこれをうまく扱ってると思ってたけど、そうじゃなかったの?

NSLinkAttributeName?

ほんとに「Link」って名前なんだよね。https://developer.apple.com/documentation/swiftui/link これ以上簡単にするのは無理なんじゃないかな。

iOSアプリの段落内でクリック可能なリンクをレンダリングするように求められた その具体的な要求自体がすでに悪いアイデアだった。

macOSを使ってるなら、WebKitはネイティブのOSフレームワークだよ。MarkdownをレンダリングするのにWebKitを使うのは全然適切だと思う。でも、もしすべてをWebKitでレンダリングしてるなら、それは馬鹿げてる。PDFKitで全部をレンダリングするのが馬鹿げてるのと同じようにね。でもMarkdownビューに関しては、WebKitは論理的な選択だと思う。後からテーブルをひっくり返して、すべてをChromiumのウェブアプリに置き換える必要はないよ。

でも、なんでWebKitを使ってリッチテキストをレンダリングすることを期待するの? HTML/CSS/JSレンダラーを使ってテキストをレンダリングするのが「完全に適切」なら、何が適切じゃないの? なんで全てをそれでレンダリングしないの?「テキストのレンダリングは完全に適切」って言いながら、「全てをレンダリングするのは馬鹿げてる」ってどういうことなのか理解できないよ。

そうそう、実はこれが今進行中の解決策なんだ:最終的なMarkdownをレンダリングして、WebKitを通じてストリーミングするってやつ。で、そうだね、macOSではWebKitはネイティブOSフレームワークだよね。そういう意味では「ネイティブ」だ。でも、リッチテキスト、Markdown、選択、タイポグラフィ、長文フォーマットされたコンテンツをちゃんと扱いたいなら、ウェブ技術が唯一の実行可能な選択肢になっちゃうと思う。MarkdownビューにWebKitを使うのが間違ってるとは言わないよ。むしろ、それが一番合理的な選択肢だと思う。ただ、ここでの「ネイティブ」な解決策は、実際にはウェブレンダリングの解決策に過ぎないんだよね。コストがかかるし、各WKWebViewは独自のパフォーマンスとメモリオーバーヘッドを持ってるから、どこにでもWKWebViewを撒いて「無料のネイティブmacOSコンポーネント」として扱うことはできないよ。この答えがフラストレーションの元なんだ。こういうUIに関しては、SwiftUI/AppKit/TextKitは「ただWebKitを使う」よりも良いクリーンでモダンなコンポーザブルな道を提供してくれないんだよ。

OPは「ネイティブ」=Swift/ObjCのプリミティブだけを使うって考えてるのかな?WebKitは他のプラットフォームにもあるから、ズルしてるってこと?だったらJava使ったほうがマシじゃない?

MarkdownをレンダリングするのにWebKitを使うのは全然適切だと思うけど、そうじゃないの?説明が必要だね。

HTMLエンジンがリッチテキストのレンダリングに関してネイティブUIライブラリより優れているなら、UIが必要とする最も難しいことの一つだよね。じゃあ、ボタンやテキストフィールドみたいな簡単なものをレンダリングするのにも使わない理由はないじゃん?それに、OS Xは長い間DisplayPDF/QuartzでUIをレンダリングしてたし。

シンプルな画面を離れると、これらの「ネイティブ」なものがどれだけ未熟かがわかるよね。 そうだね。人々が十分な努力を注がないものに、成熟することを期待するのは無理だよ。みんなウェブ技術にロックインされてるのは、そこに多くの努力が注がれているから。実際、ネイティブを見て「まだ発展してない」と言って、さらにウェブの開発に戻るっていうサイクルが繰り返されてる。ブラウザで「うまく動く」ものがあるから、ネイティブを改善しようとする人はほとんどいないよね。

確かに、ネイティブUI開発キットは商業製品だよね?それを人に売るのが彼らの仕事であって、人が自分を売り込むのが仕事じゃないよね?ウェブ関連のものが成熟してる理由の一部は、大手商業OSメーカーが時代に追いつこうとしないからだと思う。WindowsのUIキットは本当にめちゃくちゃだよ。

同意。彼は基本的に、SwiftでMarkdownを扱うのが遅いって文句言ってるだけで、誰もまだそのために多くの努力をしてないのにね。それなのに、自分ではその改善に貢献するつもりもないみたい。

以前はパフォーマンスがネイティブAPIを使う理由だったけど、今はそうでもないみたい。ブラウザのレンダリングエンジンはかなり成熟してるし、GPUアクセラレーションもかなり進んでる。さらに、膨大なウェブアプリによって10年以上もストレステストされてるからね。一方で、SwiftUIは特に速いとは感じない。Appleの最新のSystem Preferencesのリライトは、UIをチェックボックスの行に簡素化しちゃったし、セクション間の切り替えは、us-east-1からウェブページを読み込むよりも遅れることがあるよ。

ブラウザのレンダリングエンジンは今やかなり成熟していて、GPUアクセラレーションもかなり進んでるし、膨れ上がったウェブアプリによって10年以上のストレステストも受けてる。でも、ネイティブアプリと軽いブラウザアプリの間には、特に低スペックのデバイスでは明らかな違いがあるよね。俺は元々ウェブ開発者だったけど、ここ6〜12ヶ月でネイティブのクロスプラットフォームアプリを開発し始めたんだけど、簡単なものでもパフォーマンスの差がかなり大きいんだよね、変なことに。

一方でSwiftUIは特に速いとは感じないね。それはSwiftUIが特に優れているわけじゃなくて、ウェブレンダリングがネイティブと同じくらい良いわけでもないから。AppKitはパフォーマンスとリソース消費の面で、どちらにも勝ってるよ。

今やRAMの使用量が、ネイティブAPIをウェブビューより好む主な理由だね。

ここで問題なのはSwiftUIであって、一般的なネイティブアプリじゃないよ[1][2]。俺はQt C++とQMLでネイティブアプリを書いたけど、似たようなウェブアプリよりもかなり速くて、RAMもかなり少なくて済むことを示したんだ[3]。だから、一般的にウェブアプリは遅くて、よく設計されたネイティブアプリよりもリソースを多く使うんだよ。 [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

確かに、シンプルなウェブアプリにはいいかもしれないけど、複雑なアプリだと明らかに遅くなるよ。Jiraみたいなモンスターアプリの話じゃなくて、VS Codeみたいに最適化されたアプリでも、パフォーマンスの上限がネイティブアプリより低いんだよね。

SwiftUI(特に「SwiftUI」ってわけじゃなくて、パラダイム全体のことだけど)は、大量のデータのインクリメンタルな変更には向いてない。SwiftUIはその点がすごく苦手で、インクリメンタルな変更をもっと最適化するための良いAPIも提供してない。だから、Appleが今まで使えるSwiftUIのテキストビューコンポーネントを出さなかった理由の一つなんだ。

システム環境設定も時々WebViewをレンダリングすることがあるよね。特にAppleアカウントの設定で。

ブラウザのレンダリングエンジンは今やかなり成熟してるし、GPUアクセラレーションもかなり進んでる。しかも、膨れ上がったウェブアプリによって10年以上もストレステストされてきた。古いハードウェアでは動作がひどいけどね。古いChromebookはたくさんあるし、軽い使い方や特定の用途にはそこそこスペックがあるマシンだよ。でも、ブラウザはそれらでは全然動かない。

これはActiveX/nacl/wasmなどの議論を繰り返してるだけだね。何十年も、人々はブラウザ環境に安全にデプロイできる速いコードをどうやって入れるか悩んでた。そしたらGoogleのV8チームが「じゃあ、JavaScriptをめちゃくちゃ速くしちゃえばいいんじゃない?」って聞いたわけで、今に至る。スカラーなスクリプト言語にうまくマッピングできない環境ではネイティブコードの余地はまだあるけど、あまり多くはない。基本的に、みんなその問題が存在したことを無視するのが一番いい。レンダリング側はもう少し時間がかかったけど、今ここにいる。ハードウェアをDOMで抽象化されてない方法で利用する必要がある特別なアプリにはまだ余地があるけど(ゲームだけじゃなくて、近いけどね)。でも、一般的な「GUIが必要」な問題には?うん、Electron使えばいいよ。

でも、シンプルなことがうまく動かない:Markdownを使ったチャットと、メッセージ全体を選択する機能。ごめん、ちょっとおかしいと思う。SwiftUIで成熟したMarkdownレンダラーを活用できるよ。https://github.com/gonzalezreal/swift-markdown-ui とその次世代の置き換え https://github.com/gonzalezreal/textual を見てみて。自分も使ったことがあって、問題なかったよ。SwiftやSwiftUIが好きじゃないバカだけど、Objective-Cの方が好きだったけど、LLMの助けなしでこれをやり遂げたよ。

新しいテキストをストリーミングしながら、ちらつかずに処理できるの?

最初のやつはClaudeのiOSアプリで、まあまあ動いてるみたい。テキストを選択したり、ストリーミングもできるみたいだよ。

今日の早い段階でTextualを試してみたけど、あまり良い結果じゃなかったな。 - 静的なMarkdownのスクロールが新しいフォーカスプローブに失敗。結果: p95 18.86 ms 対 16.7 msの予算、最大232.49 ms。 - 長いMarkdown/コードの更新パスも失敗。結果: p95 59.33 ms 対 16.7 ms、最大75.94 ms。これは大きなリッチテキストの更新時の別のストレスケースだね。 - 長い履歴のスケーリングは技術的にはパスしてるけど、数字がスムーズじゃない: - 120回: 合計p95 21.35 ms - 500回: 合計p95 23.11 ms - 1000回: 合計p95 36.77 ms 技術的には悪くはないけど、俺の解決策よりちょっと遅いし、SwiftUIに関連するパフォーマンスのギャップが似てるんだよね。

うん、これは難しい問題だね。Qt C++とQMLを使ってブロックエディタをゼロから作ったことで、どうやってこれを解決したかを詳しく書いたよ[1]。似たような問題に直面した - 異なるブロック間の選択、カーソルの下にMarkdownを表示すること、デリゲートサイズの変化など。学んだことを使って、ストリーミングMarkdownパーサーを持つネイティブLLMクライアントを作ってる[2]。 [1] https://rubymamistvalove.com/block-editor [2] https://www.get-vox.com

最近、TextKit 2を使ったiOS用のテキストエディタをリリースしたんだけど、5,000行のファイルでもすごくパフォーマンスが良いんだ(Project Gutenbergの『モビー・ディック』でテストした)。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/

2012年にインタラクティブノベル(NSStringと属性)、ローグライクの低レベルグリフ(APIは非推奨)、SwiftUIでの2つのチャットアプリ(フォーマット用にマークダウン対応)、iOSのトリックを使ったアイドルゲームを作った経験があるけど、この回答をまとめた通り、スキルの問題だと思う。

つまり、著者のスキルの問題って感じだね。

面白い事実: これがAppleが以前やっていた方法だよ。古いバージョンのmacOS/AppKitは、ネイティブのNSTextField内でリッチテキストをレンダリングするためにWebKitを使っていたんだ。テキストは難しいってことが分かったよ :) それに、ネイティブのWebViewはすごく速くて軽量だから、テキストレイアウトエンジンとして使うのは全然理にかなってる。テーブルの各行に別々のWebViewを使っても、素晴らしいパフォーマンスが得られるよ。iMessage for MacもWebViewを使ってたし、Adiumもそうだった。リッチ/マークアップされたテキストをレンダリングするなら、HTMLは絶対に正しいツールだよ。

...ただ、この記事の論理はちょっと変だね: 1. 複雑なネイティブテキストレンダリングが難しいことを発見 2. 低レベルな方法でテキストをレンダリングして、ネイティブインタラクションを(再)実装しなければならないと文句を言う 3. WebKitを試したらうまくいった! 4. WebKitを捨てる?? 5. ネイティブインタラクションを再実装しなければならない?? 個人的には(3)で止めてたと思う。