Web開発
Dactylの仕組み
要約
Dactylは、ネイティブアプリ開発における妥協をなくすことを目指したクロスプラットフォーム開発プラットフォームです。特に、SwiftUIのレンダリングエンジンをWebAssembly(Wasm)に移植し、iOSシミュレータの機能をブラウザ上で再現することに成功しました。これにより、開発者はMacを持たずにiOSアプリのプレビューをWeb上でリアルタイムに行えるほか、AndroidでもネイティブUIコンポーネントとしてレンダリングできます。記事では、このWasmベースのSwiftUIレンダラーの内部構造、動的リンクの仕組み、そして開発ループを高速化するエージェントベースの自動テストについて解説しています。
全文翻訳
Dactylの仕組み
Divy Srivastava · 2026年8月26日
アプリ開発は、LLMにとって常にWeb開発よりも困難でした。アプリはプラットフォームのスタイル規範やレイアウトを持つ傾向があり、特定の見た目と感触が必要です。人々はアプリが「ネイティブ」でデバイス用に作られていると感じることができます。さらに、Webはアプリやアプリストアとは異なり、オープンで普遍的であるため、LLMのトレーニングセットには比較してデータが少なくなります。
多くのバイブコーディングプラットフォームは、本質的に妥協することでこの問題の解決を試みてきました。Expo/React Native、Flutter、あるいは単なるWebビューを使用してアプリを生成します。しかし、これらのアプローチのいずれも最適なユーザーエクスペリエンスを提供しないことがわかりました。クロスプラットフォーム互換性、ネイティブウィジェット、パフォーマンス、または機能のいずれかを常に犠牲にすることになります。
私たちは、これらの妥協なしにネイティブアプリを構築するのを支援するためにDactylを作成しました。
本日、DactylのクロスプラットフォームSwiftUIレンダラー—iOSシミュレータのWasmポート—の内部構造と、AppleエコシステムをAndroidおよびWebにもたらす方法を紹介します。
Web上でのiOSプレビュー
Dactylでは、Swiftのシンプルさと明確に定義されたレイアウトセマンティクスから、主要言語として選択しました。実際、Dactylは当初、iOS専用の開発プラットフォームとして設計されていました。
MacなしでiOSアプリをプレビューするということは、通常、Macをレンタルして実際のシミュレータをストリーミングすることを意味します。起動、インストール、そしてアプリを見る前に起動し、作業している間は時間課金されます。
そのアプローチは、予算が許す限りしかスケールしません。しかし、ブラウザ内にシミュレータがあったらどうでしょうか?それは無限にスケールするでしょう。そこで私たちはそれを構築しました。
Dactylのプレビューエンジンは、SwiftでのSwiftUIの再実装であり、アプリが利用するフレームワークも含まれています。Charts、SpriteKit、SceneKit、RealityKit、ARKit、MapKit、StoreKit、MusicKit、PencilKit、WidgetKit、UIKit、CoreGraphics、Metal、Visionなど、多くのモジュールが実装されています。
アプリはWasmにビルドされ、AppleのSwiftUIではなく、私たちのSwiftUIにリンクされます。
エンジンは描画コマンドのバイナリストリームを独自の線形メモリに出力し、そのプロトコルはCanvas2D上に描画するJavaScriptホストによって読み取られます。
レイアウトは、コマンドが書き込まれる前に完全に解決され、リコンサイラーはパス間でビューツリーを維持し、ライブで何かを読み取ったビューのみをダーティとしてマークします。このフレームでは、アニメーション、スクロールコンテナなどが該当します。ホストは各レイヤーをオフスクリーンキャンバスに一度ラスタライズし、その内容のハッシュをキーとして、後続のフレームで合成します。
これにより、Dactylは複雑なSwiftUIレイアウトを、インストール手順や追加デバイスなしで、ブラウザ上でスムーズかつ効率的にレンダリングできます。(ただし、それらすべてを処理することもできます。)
レイアウトツリー
読み込み中...
それが解決されるフレーム
Swiftの動的リンク
SwiftWasmは静的にリンクされます。これは私たちにとって理想的ではありません。なぜなら、エンジンは約100MBであり、静的リンクは編集ごとに数百MBをブラウザにリンクして出荷することを意味するからです。
アプリ自体ははるかに小さいので、ビルドを分割しました。エンジンと標準ライブラリはベースモジュールとして一度リンクされ、コンテンツアドレス指定されるため、ブラウザはそれを一度だけフェッチしてコンパイルし、編集間で保持します。
アプリは、位置独立のサイドモジュールとして、8KBから300KBで独自にコンパイルされます。ロード時に、実行時メモリオフセットに配置され、そのインポートはベースのエクスポートにバインドされ、そのSwift型メタデータはベースのランタイムに登録されます。スワップは実行中のアプリの下で行われます。@Stateはスナップショットされ、前後に復元されるため、スクロール位置、テキストフィールド、アプリデータは編集間で保持されます。
examples/weather/ContentView.swift
89 Color(red: 0.42, green: 0.69, blue: 0.95)],36 KB0.28 s
Android: Jetpack Compose
Androidでは、エンジンはSwift Android SDKを使用してaarch64-unknown-linux-android24用にネイティブにコンパイルされ、共有ライブラリになります。Jetpack Composeホストがそれをロードし、JNI経由で駆動します。コマンドストリームはネイティブメモリから直接読み取られるため、コピーなしで、Composeホストはブラウザと同じバイトをデコードします。
ブラウザホストがキャンバスにコントロールをペイントする場所で、Composeホストは実際のMaterialスイッチ、テキストフィールド、または日付ピッカーをその矩形にマウントし、ネイティブフォーカス、リップル、キーボード、アクセシビリティを備えています。
下の両方の電話は、同じ71行のSwiftUIソースを実行しています。左は起動したiPhoneシミュレータ、右はAndroidエミュレータ上のAPKです。
iOS
Android
PICKER
セグメント化されたUIKitセグメントコントロール対Materialセグメントボタン、チェックマーク
SF SymbolsとMaterial Symbolsは異なるアイコンセットで名前も異なるため、生成されたマッピングテーブルが両方のホストをカバーします。Composeの密度は、デバイスの密度ではなく、エンジンのレイアウトスケールに固定されるため、コントロールはエンジンが解決した矩形に正確に着地します。アプリごとのAPKはホストを再ビルドしません。共有ライブラリ、アプリ名、アイコンは、事前にビルドされたテンプレートにスタンプされます。
iPadは同じエンジンで、異なるデバイス記述子を使用します。ビューポート、セーフエリア、サイズクラスが変更され、SwiftUIのアダプティブレイアウトが残りを処理します。
開発ループ
Appleシミュレータ
私たちのエンジン
違い
エージェントがエンジンのフレームワークソースを編集中
右の編集中
Charts/ChartRender.swift
キューに入りました
SwiftUI/Core/ShapeStyle.swift
キューに入りました
SwiftUI/Modifiers/TextLayoutModifiers.swift
キューに入りました
SwiftUI/Generated/LayoutParams.swift
編集中
SwiftUI/Core/ColorGradient.swift
編集中
Charts/ChartPlots.swift
編集中
SwiftUI/Modifiers/ScrollModifiers.swift
キューに入りました
SwiftUI/Core/SemanticColors.swift
完了しました
UIKit/GestureScrollControl.swift
編集中
Charts/ChartViewModifiers.swift
完了しました
SwiftUI/Core/Animatable.swift
キューに入りました
CoreGraphics/CGPath.swift
完了しました
…そして残りは赤
再レンダリング、差分を取り、同意するまで
エンジンの開発は、エージェントのフリートを使用して高度に自動化されています。エージェントはAppleのSDKアーティファクトを使用してAPIリファレンスを構築します。次に、起動したiOSシミュレータでさまざまなシーンをレンダリングし、キャプチャして、エンジンの出力とピクセルごとに差分を取ります。新しいiOSが出荷されると、ソース・オブ・トゥルースを新しいシミュレータに対して再レンダリングします。
スクリーンショットは動くものをすべて見逃すため、フィルムストリップランナーが仮想クロックを駆動します。固定ステップでエンジンをティックし、タイムラインでフレームをサンプリングし、正確な瞬間に合成タップとキープレスを発火させます。
ゲームの場合、Nフレームでキャラクターが画面上のどこに実際にいるかなど、状態とイベントの差分を取ります。私たちのエンジンと実際のシミュレータは、同じフレームごとのノードスキーマ(位置、回転、スケール、アルファ、物理速度)を出力し、軌跡はノードごとにフレームごとに差分を取ります。
各差分は、問題を特定するビジュアル大規模言語モデルに渡され、次にエージェントがエンジンのフレームワークソースを編集して差分がしきい値未満になるまで展開されます。これは驚くほど効果的なプロセスであり、SwiftUIのような継続的に移動するフレームワークの実装に非常に近づいています。
Dactylの動作を見る
舞台裏の技術的な詳細は以上です。Dactylがデバイスにアプリをインストールしてテストするのをどのように支援するか、あるいはApp Storeにアプリをリストするのをどのようにガイドするかについては、まだ触れていません。しかし、おそらくご自身でご覧いただくのが一番でしょう。
アプリを説明するだけで、ここで説明したすべてがそれを構築し、ブラウザでライブ表示します。今すぐDactylを試してみてください。