Skip to content

formspec-swift: SwiftUI renderer for iOS/macOS #5

@mikewolfd

Description

@mikewolfd

Motivation

iOS and macOS apps need to render formspec forms as native SwiftUI views. A Swift Package that bridges to formspec-engine via a hidden WKWebView provides full spec compliance without reimplementing the engine in Swift.

Architecture

Three layers:

  1. Bridge — Hidden WKWebView loads an HTML bundle (esbuild'd formspec-engine JS + inlined WASM). Swift sends EngineCommand JSON messages, receives EngineEvent JSON messages via WKScriptMessageHandler.
  2. State@Observable classes (FieldState, FormState) updated from bridge events. SwiftUI views react automatically via observation.
  3. RendererFormspecForm view walks the LayoutNode tree. Component map ([String: any FormspecComponent]) provides default SwiftUI components, overridable by the consumer.

Scope

  • Swift Package (SPM) targeting iOS 17+ / macOS 14+
  • Hidden WKWebView bridge with typed EngineCommand/EngineEvent protocol
  • @observable state objects (FieldState, FormState, LayoutNode)
  • Auto-renderer that walks LayoutNode tree with component map
  • Default SwiftUI components for core field types
  • XCTest suite for bridge, state, and renderer layers

Status

Implementation plan and design spec complete. Initial code in packages/formspec-swift/.

Key References

  • Design spec: thoughts/specs/2026-03-25-formspec-swift-design.md
  • Implementation plan: thoughts/plans/2026-03-25-formspec-swift.md

Tech

  • Swift 5.9+, SwiftUI, iOS 17+ @observable
  • WKWebView, WKScriptMessageHandler
  • esbuild for HTML bundle
  • XCTest

Acceptance Criteria

  • Swift Package builds and resolves via SPM
  • Hidden WKWebView bridge initializes formspec-engine and processes commands
  • State objects update reactively from engine events
  • Auto-renderer produces native SwiftUI views from a formspec definition
  • Component map is overridable by consumers
  • XCTest suite covers bridge, state, and renderer layers

Part of #4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions