Dactyl: Cách biến SwiftUI thành ứng dụng native đa nền tảng ngay trong trình duyệt
Dactyl giới thiệu kiến trúc đột phá: một bản port Wasm của iOS Simulator chạy ngay trong trình duyệt, cho phép render SwiftUI thuần native trên web và Android. Bài viết phân tích cơ chế dynamic linking, bộ điều phối view tree, và quy trình tự động hóa phát triển engine bằng AI agents — hứa hẹn thay đổi cuộc chơi vibe-coding cho mobile.

Dactyl: Cách biến SwiftUI thành ứng dụng native đa nền tảng ngay trong trình duyệt
Dactyl không chỉ là một công cụ vibe-coding thông thường — nó là một bản port Wasm của iOS Simulator chạy trực tiếp trong trình duyệt, render SwiftUI thuần native trên cả web lẫn Android. Bằng cách tái hiện toàn bộ framework Apple và dùng AI agents để tự động hóa quá trình phát triển engine, Dactyl hứa hẹn giải quyết bài toán "native app" mà các nền tảng sinh mã khác đang bỏ ngỏ. Đáng chú ý, engine của nó được phát triển bằng chính AI — một vòng lặp tự động diff và sửa lỗi so với simulator thật.
Vấn đề: App native luôn khó hơn web cho LLM
Phát triển ứng dụng di động luôn khó hơn web đối với các mô hình ngôn ngữ lớn (LLM). Lý do rất đơn giản: app có những chuẩn mực về giao diện và bố cục riêng cho từng nền tảng. Người dùng có thể cảm nhận ngay một app có "native" và được tinh chỉnh cho thiết bị của họ hay không. Hơn nữa, web mở và phổ quát trong khi app và app store thường đóng — điều này khiến LLM có ít dữ liệu huấn luyện hơn cho lĩnh vực này.
Nhiều nền tảng vibe-coding đã cố giải quyết bằng cách "thỏa hiệp": tạo app bằng Expo/React Native, Flutter, hoặc thậm chí chỉ là webview. Nhưng không cách nào trong số đó mang lại trải nghiệm tối ưu — bạn luôn phải hy sinh một thứ gì đó: khả năng đa nền tảng, widget native, hiệu năng, hay tính năng.
Dactyl được tạo ra để xây dựng app native không cần thỏa hiệp. Hôm nay, chúng ta sẽ khám phá cơ chế hoạt động bên trong của bộ render SwiftUI đa nền tảng của họ — một port Wasm của iOS Simulator — và cách họ đưa hệ sinh thái Apple lên Android và web.
iOS preview trên web: Giả lập ngay trong trình duyệt
Điểm mấu chốt của Dactyl là chọn Swift làm ngôn ngữ chính vì sự đơn giản và ngữ nghĩa bố cục rõ ràng. Ban đầu, Dactyl được thiết kế như một nền tảng phát triển độc quyền cho iOS.
Preview một app iOS mà không có Mac thường đồng nghĩa với việc thuê một chiếc Mac và stream simulator thật về — một quá trình tốn kém: boot máy, cài đặt, khởi động app trước cả khi bạn thấy được giao diện, và chi phí tính theo thời gian sử dụng. Cách này chỉ mở rộng theo ngân sách của bạn.
Nhưng nếu bạn có thể có một simulator ngay trong trình duyệt thì sao? Điều đó sẽ mở rộng vô hạn. Dactyl đã làm được điều đó.
Giao diện chỉnh sửa trước khi dùng Dactyl
Engine preview của Dactyl là một sự tái hiện SwiftUI bằng Swift, cộng với các framework mà app thường dùng. Rất nhiều module đã được triển khai: Charts, SpriteKit, SceneKit, RealityKit, ARKit, MapKit, StoreKit, MusicKit, PencilKit, WidgetKit, UIKit, CoreGraphics, Metal và Vision.
App được build thành Wasm và liên kết với SwiftUI của Dactyl thay vì của Apple. Engine phát ra một luồng lệnh vẽ nhị phân vào bộ nhớ tuyến tính riêng, và giao thức này được một host JavaScript đọc để vẽ lên Canvas2D.
Kết quả sau khi chỉnh sửa với Dactyl
Bố cục được giải quyết hoàn toàn trước khi bất kỳ lệnh nào được ghi. Một bộ điều phối (reconciler) giữ cây view giữa các khung hình, chỉ đánh dấu "dirty" những view đọc dữ liệu trực tiếp trong khung hình đó: animation, container cuộn, v.v. Host rasterize mỗi layer một lần lên một canvas offscreen được khóa bằng hash nội dung, sau đó composite lại ở các khung hình sau.
Nhờ vậy, Dactyl có thể render các bố cục SwiftUI phức tạp một cách mượt mà, ngay trong trình duyệt, không cần cài đặt hay thiết bị bổ sung.
Dynamic linking: Chìa khóa cho tốc độ
SwiftWasm liên kết tĩnh — điều này không lý tưởng cho Dactyl, vì engine ~100 MB. Liên kết tĩnh nghĩa là phải gửi hàng trăm MB đến trình duyệt mỗi lần chỉnh sửa.
Giải pháp: tách build. Engine và thư viện chuẩn được liên kết một lần thành một module cơ sở (content-addressed), trình duyệt tải và biên dịch một lần duy nhất, và giữ lại qua các lần chỉnh sửa. App của bạn tự biên dịch thành một side module độc lập với vị trí (position-independent), chỉ từ 8 KB đến 300 KB.
Khi tải, side module được đặt ở một offset bộ nhớ runtime, các import được liên kết với export của module cơ sở, và type metadata của Swift được đăng ký với runtime của module cơ sở. Việc hoán đổi diễn ra ngay khi app đang chạy — @State được snapshot trước và khôi phục sau, nên vị trí cuộn, text field và dữ liệu app đều được giữ nguyên qua các lần sửa.
Android: Jetpack Compose làm host
Trên Android, engine biên dịch native cho aarch64-unknown-linux-android24 bằng Swift Android SDK thành một shared library. Một host Jetpack Compose tải nó và điều khiển qua JNI. Luồng lệnh được đọc trực tiếp từ bộ nhớ native — không sao chép — nên host Compose giải mã cùng một byte mà trình duyệt dùng.
Điểm thú vị: trong khi host trình duyệt vẽ một control lên canvas, host Compose lại mount một switch, text field hoặc date picker Material thật sự vào đúng hình chữ nhật đó, với focus, ripple, bàn phím và accessibility native.
Native iOS với Dactyl
Cả hai chiếc điện thoại dưới đây đang chạy cùng một mã SwiftUI 71 dòng: bên trái là iPhone simulator đã boot; bên phải là file APK trên Android emulator.
Native Android với Dactyl
SF Symbols và Material Symbols là hai bộ icon khác nhau với tên khác nhau, nên có một bảng ánh xạ tổng hợp cho cả hai host. Density của Compose được ghim theo tỷ lệ bố cục của engine thay vì theo thiết bị, để các control nằm chính xác vào hình chữ nhật mà engine đã giải quyết. Mỗi APK không cần build lại host — shared library, tên app và icon được đóng dấu vào một template có sẵn. iPad cũng dùng cùng engine, chỉ khác device descriptor: viewport, safe areas và size classes thay đổi, và layout thích ứng của SwiftUI sẽ lo phần còn lại.
Phát triển engine bằng AI agents
Một điểm cực kỳ đáng chú ý: engine của Dactyl được phát triển tự động hóa bằng một đội agents. Quy trình như sau:
- Agents dùng SDK artifacts của Apple để xây dựng một API reference.
- Render nhiều cảnh khác nhau trong iOS simulator đã boot, chụp lại, và diff pixel-by-pixel với output của engine.
- Khi iOS mới ra, họ render lại "nguồn sự thật" với simulator mới.
- Với ảnh tĩnh không bắt được chuyển động, một filmstrip runner điều khiển một đồng hồ ảo: tick engine theo các bước cố định, lấy mẫu frame theo timeline, và bắn các cú chạm và phím giả vào đúng thời điểm.
- Với game, họ diff trạng thái và sự kiện: engine và simulator thật cùng dump schema node mỗi frame (vị trí, xoay, tỷ lệ, alpha, vận tốc vật lý), và các quỹ đạo được diff theo từng node, từng frame.
Mỗi diff được đưa cho một visual LLM để xác định vấn đề, sau đó phân tán các agents về sửa mã nguồn framework của engine cho đến khi diff dưới ngưỡng cho phép.
Đây là một quy trình hiệu quả đến bất ngờ, và nó đưa chúng tôi rất gần với việc triển khai một framework liên tục chuyển động như SwiftUI.
Tương lai của vibe-coding native
Dactyl cho thấy một hướng đi mới: thay vì chấp nhận webview hay React Native, chúng ta có thể tái tạo chính hệ sinh thái native ngay trong trình duyệt, và dùng AI không chỉ để sinh mã app mà còn để phát triển chính engine render. Với khả năng chạy SwiftUI trên cả iOS, Android và web từ một mã nguồn, Dactyl có thể là nền tảng khiến giấc mơ "write once, run natively everywhere" trở nên thực tế hơn bao giờ hết — đặc biệt có ý nghĩa với cộng đồng developers Việt Nam vốn đang rất quan tâm đến các công cụ hỗ trợ phát triển ứng dụng di động nhanh và hiệu quả.


