Dactyl如何工作
摘要
Dactyl是一个开发平台,通过提供跨平台的SwiftUI渲染器来构建原生应用,使用Wasm实现高效的Web预览,并通过动态链接实现无缝更新。
暂无内容
查看缓存全文
缓存时间: 2026/08/28 18:30
# Dactyl 工作原理 · Dactyl
来源:https://dactyl.dev/blog/how-dactyl-works/
对于大语言模型而言,应用开发历来比网页开发更具挑战性。应用需要遵循特定平台的风格规范与布局,且必须呈现出相应的外观与质感。用户能明显感知到一个应用是否“原生”并为其设备量身打造。此外,网页在开放性和通用性方面通常远超应用与应用商店,这意味着大语言模型在训练数据中涉及应用开发的信息相对较少。
许多氛围编程平台试图通过妥协来解决这个问题。它们使用 Expo/React Native、Flutter,甚至直接基于 WebView 来生成应用。但我们发现这些方法都无法提供最佳的用户体验——无论是跨平台兼容性、原生组件、性能还是功能特性,总得有所取舍。
**我们创建 Dactyl 正是为了帮助你在无妥协的情况下构建原生应用**。
今天,我们将揭秘 Dactyl 跨平台 SwiftUI 渲染器的内部工作原理——它是 iOS 模拟器的 Wasm 移植版本——以及我们如何将 Apple 生态系统带到 Android 和 Web 上。
## 在网页上预览 iOS 应用
我们选择 Swift 作为 Dactyl 的主要语言,源于其简洁性和明确的布局语义。事实上,Dactyl 最初设计为仅支持 iOS 的开发平台。
要在没有 Mac 的情况下预览 iOS 应用,通常意味着需要租用一台 Mac 并将真实模拟器画面回传:在你第一次看到应用之前,就需要经历启动、安装和运行,且只要持续工作就会持续计费。
这种方式的扩展性仅限于你的预算。但如果你能在浏览器中拥有一个模拟器呢?那它的扩展性几乎是无限的。于是我们构建了它。
Dactyl 的预览引擎是用 Swift 重新实现的 SwiftUI 及应用常用框架。我们实现了大量模块,包括 Charts、SpriteKit、SceneKit、RealityKit、ARKit、MapKit、StoreKit、MusicKit、PencilKit、WidgetKit、UIKit、CoreGraphics、Metal 和 Vision。
应用被编译为 Wasm,并链接到我们的 SwiftUI 而非 Apple 的版本。
引擎将其自身的线性内存中发出绘制命令的二进制流,该协议由一个 JavaScript 宿主读取,并将其绘制到 Canvas2D 上。
布局在任何命令写入之前就已完全解析,一个协调器在渲染通道之间保持视图树,并仅将那些在本帧读取动态内容的视图标记为脏视图:例如动画、滚动容器等。宿主将每一层光栅化到一个离屏画布上(基于其内容的哈希值),并在后续帧中进行合成。
所有这些意味着 Dactyl 能够直接在浏览器中流畅、高效地渲染复杂的 SwiftUI 布局,无需任何安装步骤或额外设备。(当然,它也具备处理这些额外步骤的能力。)
*布局树*加载中...
解析完成时对应的帧
## 动态链接 Swift
SwiftWasm 采用静态链接。这对我们来说并不理想,因为引擎体积约 100 MB,静态链接意味着每次编辑都需要向浏览器链接和传输数百兆数据。
应用本身要小得多,因此我们拆分了构建流程。引擎和标准库作为基础模块链接一次,基于内容寻址,因此浏览器只需获取并编译一次,便能在多次编辑间保持。
你的应用单独编译为一个位置无关的侧模块,大小在 8 KB 到 300 KB 之间。加载时,它会被放置在运行时内存偏移处,其导入项绑定到基础模块的导出项,其 Swift 类型元数据也会注册到基础模块的运行时中。这个替换过程在正在运行的应用下进行。`@State` 会在前后进行快照与恢复,因此滚动位置、文本字段和应用数据得以在编辑过程中保留。
examples/weather/ContentView.swift*89*Color\(red:0\.42,green:0\.69,blue:0\.95\)\],
36 KB0\.28 秒
## Android:Jetpack Compose
在 Android 上,引擎通过 Swift Android SDK (https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-starting.html) 为 `aarch64-unknown-linux-android24` 编译原生共享库。一个 Jetpack Compose 宿主加载它并通过 JNI 驱动。命令流直接从原生内存中读取,无需复制,因此 Compose 宿主解码的字节与浏览器完全一致。
当浏览器宿主将控件绘制到画布上时,Compose 宿主会在该矩形区域挂载一个真实的 Material 开关、文本框或日期选择器,具备原生的焦点、波纹效果、键盘和无障碍支持。
下方两部手机运行的是同一份 71 行的 SwiftUI 源代码:左侧是已启动的 iPhone 模拟器;右侧是在 Android 模拟器上运行的 APK。
构建自同一份 SwiftUI 源代码的应用,在真实 iPhone 模拟器上的截图
iOS 同样的构建版本作为单个 APK 在 Android 模拟器上运行,由 Compose 宿主绘制
Android
**选择器*分段控制*UIKit 分段控件*对比*Material 分段按钮,勾选标记**
SF Symbols 和 Material Symbols 是两套不同名称的图标集,因此我们生成了一个映射表来覆盖两个宿主。Compose 的密度固定为引擎的布局比例,而非设备的比例,因此控件能精确落在引擎解析的矩形区域内。单个应用的 APK 不会重建宿主。共享库、应用名称和图标会被写入一个预构建的模板中。
iPad 使用同一引擎,但设备描述符不同。视口、安全区域和尺寸类别会随之改变,SwiftUI 的自适应布局则处理其余部分。
## 开发流程
Apple 模拟器
Apple 模拟器我们的引擎
我们的引擎差异
差异
*代理在引擎框架源代码上操作*4 正在编辑
- `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 (https://dactyl.dev/)。
相似文章
关于使用 SwiftUI 构建 macOS 应用的 WWDC 27 更新
本文介绍了 WWDC 27 中 SwiftUI 的改进,用于构建 macOS 应用,涵盖选中状态、拖放和键盘快捷键的新 API。
在 2026 年使用 SwiftUI 构建纯正的 Mac 应用
这篇文章讲述了作者完全使用 SwiftUI 构建 macOS 应用的经验,讨论了在实现原生 Mac 体验时遇到的挑战和限制,例如选中状态和非活动窗口的行为,并得出结论:SwiftUI 在 Mac 上尚不足以构建‘纯正 Mac’应用。
Show HN: Davit,一个 Apple Containers 界面
Davit 是一个免费、开源的 macOS 原生应用,使用 SwiftUI 构建,为 Apple silicon 上的 Apple 容器平台提供用户界面,允许用户无需 Docker Desktop 即可运行 Linux 容器。
Dart Live:通过 WebAssembly 在 Web 上运行编译器、VM、分析器和热重载
Dart Live 使得 Dart 编译器、虚拟机和分析器能够完全在浏览器中通过 WebAssembly 运行,支持无需服务器的状态保持式热重载。
@Chris_Wozniczek: 一个提示。这可以通过手机、Linux、Windows 或任何有浏览器的设备完成。设置只需点击五次。文章详细介绍……
本文展示了一种方法,使用Devin AI代理和Namespace的云Devboxes,在没有MacBook的情况下构建SwiftUI iOS应用,从而能够从任何基于浏览器的设备进行开发。