Skip to content

iOS: UIKit backend — a 4th implementation of aether_ui_backend.h (AppKit as reference) #22

Description

@paul-hammant

Goal

Add a UIKit backend for iOS — a fourth implementation of the existing cross-platform widget ABI in backend/aether_ui_backend.h, alongside GTK4 (Linux/FreeBSD), AppKit (macOS, backend/aether_ui_macos.m), and Win32. The payoff: an aether-ui app written once in the trailing-block DSL runs on Linux, macOS, Windows, and now iOS — the whole app including the UI is Aether, wrapped only in a minimal Swift/Obj-C launch shell.

This is why the design already supports it: aether-ui is factored as a portable Aether DSL over a ~260-function backend ABI with pluggable native implementations. iOS is a new backend, not a rewrite of the UI layer. The AppKit backend (backend/aether_ui_macos.m, ~5,900 lines of Obj-C) is the closest reference — UIKit shares concepts with AppKit but differs in class names and layout.

Prerequisite

Depends on core Aether shipping an iOS --emit=lib target so this backend .m can be linked into an iOS app: aether-lang-dev/aether#1385 ("iOS target: --emit=lib static archive for aarch64-ios / -simulator"). That issue handles the toolchain (Tier-B SDK sysroot), Mach-O platform load commands, and stubbing App-Store-forbidden syscalls (fork/exec/spawn). This issue is the UI half.

Work

backend/aether_ui_uikit.m — implement the aether_ui_backend.h ABI (~260 _impl entry points) against UIKit:

  • Widgets: map the widget kinds to UILabel / UIButton / UIStackView / UITextField / UITableView / UIImageView / etc. (Group 1/2 widgets in the header).
  • Layout — the biggest impedance mismatch: GTK's box model and AppKit's NSStackView both differ from UIKit's Auto Layout / UIStackView. The DSL's layout intent (weights via aether_ui_widget_weight_impl, frames in ui/frames.ae) must map to Auto Layout constraints. Expect this to be the bulk of the effort.
  • Events: the ABI's closure dispatch (incl. the double-click closure at header L63, hover, context menus) maps to UIKit gesture recognizers / target-action. Touch vs. pointer: some interactions have no touch analog — hover and right-click/context-menu (aether_ui_menu_popup) degrade or remap to long-press; document the mapping.
  • Styling readback: aether_ui_widget_kind_impl / _parent_impl / _classes_impl and the CSS-alike apply/add/remove-class calls (header L156-275) — the DSL walks the live tree in-process, so these must reflect the real UIKit view tree.
  • Accessibility: map the role/name/description ABI (header L215-220) to UIAccessibility (AppKit maps to NSAccessibility, GTK to GtkAccessible, Win32 to MSAA — UIKit is the iOS analog).
  • Canvas / images: aether_ui_canvas_get_widget, the in-memory image decode path (header L292-311) → UIImage(data:) + a CALayer/UIView draw surface.
  • Windows/menus: iOS has no menu bar and a different window/scene model (UIWindow + view controllers, safe-area, rotation). aether_ui_widget_window_impl and menu APIs map awkwardly — decide per-API whether to support, remap, or no-op, and document.

The launch shell (small, in the consuming app / an example): a UIApplicationMain + AppDelegate whose didFinishLaunchingWithOptions hands off to the Aether entry point that builds the widget tree via the DSL. Xcode owns Info.plist + signing + bundling.

An example app + a simulator smoke test under apps/ or examples/, mirroring an existing small app, verified in the iOS simulator.

Caveats (genuine platform-shaping, not blockers)

  • AppKit → UIKit is not copy-paste: no menu bar, touch not mouse, view-controller lifecycle, safe-area/rotation. The DSL stays identical; the backend does the shaping.
  • App Store sandbox restrictions (no child processes, restricted networking) come from core Aether#1385's stubs — orthogonal to the UI, but a UI app that expected e.g. shelling out won't get it on iOS.

Acceptance

An existing small aether-ui app (or a new minimal one) builds against aether_ui_uikit.m + the iOS --emit=lib archive and runs in the iOS simulator: widgets render, layout responds to rotation/safe-area, tap events fire their DSL closures, with hover/right-click interactions degraded to documented touch equivalents.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions