View Markup Language (VML) is a markup language derived from SGML, designed to represent composable UI frameworks for unidirectional, server-driven rendering. This specification defines the conversion from a server-side view model, such as SwiftUI, to VML.
Specification Note 1.1: Scope The VML specification is concerned exclusively with the structure and rendering of views. It is a unidirectional standard (server-to-client). Mechanisms for handling user interactions, actions, and client-to-server state changes are explicitly outside the scope of this document.
When serving VML documents:
Request Headers:
ACCEPT: application/swiftui
Response Headers:
CONTENT-TYPE: application/swiftui
VML documents must begin with a doctype declaration. For SwiftUI documents:
<!doctype swiftui>
<vml>
<head>
<Style url="/styles/main.vss"/>
</head>
<body>
<Text>Hello World</Text>
</body>
</vml>
The doctype declaration is used for document annotation only and is ignored by parsers.
VML documents require <vml>
, <head>
, and <body>
tags.
<vml>
<head>
<Style url="/styles/main.vss"/>
</head>
<body>
<NavigationStack>
<Text>Hello World</Text>
</NavigationStack>
</body>
</vml>
The head section contains:
- Style references using the
<Style>
tag. - Template definitions for lifecycle views.
The <body>
tag is required and must contain the application's primary view hierarchy.
<body>
<TabView>
<VStack tabItem="Home">
<Text>Welcome to the Home Tab</Text>
<Button>Get Started</Button>
</VStack>
<ScrollView tabItem="Settings">
<VStack spacing="16">
<Toggle isOn="true">Dark Mode</Toggle>
<Toggle isOn="false">Notifications</Toggle>
</VStack>
</ScrollView>
</TabView>
</body>
Views for different UI lifecycle states can be defined in the <head>
section using the template
attribute.
<head>
<VStack template="disconnected" spacing="16">
<Image source="cloud.slash"/>
<Text style="font(.headline)">Connection Lost</Text>
<Text style="foregroundStyle(.secondary)">
Attempting to reconnect...
</Text>
<ProgressView/>
</VStack>
</head>
<head>
<VStack template="reconnecting" spacing="16">
<ProgressView style="progressViewStyle(.circular)"/>
<Text>Reconnecting...</Text>
<Button style="buttonStyle(.bordered)">
Cancel
</Button>
</VStack>
</head>
<head>
<VStack template="error" spacing="16">
<Image source="exclamationmark.triangle.fill"/>
<Text style="font(.headline)">An Error Occurred</Text>
<Text style="foregroundStyle(.secondary)">
Unable to complete the requested operation
</Text>
<Button style="buttonStyle(.bordered)">
Retry
</Button>
</VStack>
</head>
Specification Note 3.4: Template Invocation The VML specification defines the syntax for declaring lifecycle templates. The mechanism for triggering the rendering of these templates is the responsibility of the client-side implementation. The
template
attribute serves as a well-known identifier for the client to look up and render when its internal state changes.
SwiftUI views convert directly to VML elements with exact case preservation.
LazyVStack(alignment: .leading) {
Text("Hello")
ScrollView(.horizontal) {
Text("World")
}
}
Converts to:
<LazyVStack alignment="leading">
<Text>Hello</Text>
<ScrollView axis="horizontal">
<Text>World</Text>
</ScrollView>
</LazyVStack>
Specification Note 4.1: Client-Side View Registry A VML client implementation must maintain a registry that maps VML element tag names to initializable native
View
types. The application developer is responsible for populating this registry. The VML parser will use this registry to resolve all elements.
View initializer arguments become element attributes with exact case preservation.
NavigationLink(destination: DetailView(), isActive: true, label: "Settings")
Converts to:
<NavigationLink destination="DetailView" isActive="true" label="Settings" />
Specification Note 4.2: Type-Aware Deserialization All attribute values are passed as strings. The client is responsible for performing type-aware deserialization. By introspecting the initializer of the registered
View
type, the client must parse the string attribute ("true"
) into the required native type (Bool
).
VML requires explicit boolean values for attributes.
Toggle("Light", isOn: true)
Toggle("Dark", isOn: false)
Converts to:
<Toggle isOn="true">Light</Toggle>
<Toggle isOn="false">Dark</Toggle>
Standard HTML character entity encoding is required for special characters. JSON encoding is allowed only for arrays and lists.
The id
modifier is the only modifier that converts to a root-level attribute.
Text("Hello")
.id("greeting")
.padding()
Converts to:
<Text id="greeting" style="padding()">Hello</Text>
All other SwiftUI view modifiers are converted into a single, semi-colon-delimited style
attribute. The order of modifiers must be preserved as it is significant.
Text("Hello")
.padding()
.foregroundStyle(.green)
Converts to:
<Text style="padding(); foregroundStyle(.green)">Hello</Text>
View builder closures in modifiers use template references.
VStack {
RoundedRectangle(cornerRadius: 8)
.overlay(alignment: .topLeading) {
Star(color: .red)
}
}
Converts to:
<VStack>
<RoundedRectangle
cornerRadius="8"
style="overlay(alignment: .topLeading, content: :starOverlay)"
>
<Star color="red" template="starOverlay"/>
</RoundedRectangle>
</VStack>
The attr
helper is used to create parameterized styles by referencing the value of another attribute on the same element.
VStack {
}
.navigationTitle("Home")
Converts to:
<VStack navTitle="Home" style="navigationTitle(attr(:navTitle))" />
Specification Note 5.1: Purpose of
attr
Theattr
helper's primary purpose is to allow a single style class, defined in a VSS stylesheet, to be reused across multiple elements with dynamic data. It separates the static structure of a style from the dynamic data provided on the element.
Styles can be extracted into separate stylesheets and referenced using class
attributes.
<Button class="primary-button">Submit</Button>
Specification Note 6.1: Client-Specific VSS The VML standard does not define the syntax or parsing rules for the content of View Stylesheet (
.vss
) files.
- Syntax: The VSS syntax is client-specific and should be consistent with the styling conventions of the target native UI framework.
- Parsing & Cascade: Each client implementation is responsible for parsing its respective VSS format. The rules of precedence between inline
style
attributes andclass
attributes are an implementation detail of the client.
This section provides further examples of how different SwiftUI initializer patterns are converted to VML.
A standard initializer with labeled arguments directly maps to attributes.
/// Creates a toggle that generates its label from a string.
public init(_ title: String, isOn: Binding<Bool>)
// SwiftUI Usage
Toggle("Switch Theme", isOn: $isDarkMode)
Converts to (assuming isDarkMode
is true):
<Toggle isOn="true">Switch Theme</Toggle>
Different initializers for a View can be mapped to different VML attributes to remove ambiguity.
extension Button where Label == Text {
public init(_ title: String, action: @escaping () -> Void)
public init(_ titleKey: LocalizedStringKey, action: @escaping () -> Void)
}
// SwiftUI Usage
Button("Save") { }
Button(LocalizedStringKey("welcome.message")) { }
Can be converted by introducing a localized
attribute:
<Button>Save</Button>
<Button localized="true">welcome.message</Button>
- Elements must be properly nested and closed.
- Template names must be unique only among sibling elements.
- Templates must be defined as direct children of the element referencing them.
- All attribute values must be properly encoded.
- Member expressions within style values must retain their leading dot (e.g.,
.green
). - Only arrays and lists may be JSON-encoded in attribute values.
- All
attr
references must point to existing attributes on the current element. - Boolean attributes must have explicit values.
- Application views must be contained within the
<body>
tag. - Lifecycle templates must be defined in the
<head>
section. - VML clients must implement a View Registry for mapping element tags to native View types.
- File extension:
.vml
- Encoding: UTF-8
- Whitespace: Whitespace is preserved within text content.