Create SwiftUI interfaces from JSON component trees. DynamicUI supports nested layouts, interactive controls, runtime value updates, conditional strings, and a focused set of SwiftUI modifiers.
- Swift 5.9+ (Xcode 15+)
- iOS 15+, macOS 12+, tvOS 14+, watchOS 8+, macCatalyst 15+, visionOS 1.0+
Add DynamicUI using Swift Package Manager:
dependencies: [
.package(url: "https://github.com/0xWDG/DynamicUI.git", branch: "main"),
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "DynamicUI", package: "DynamicUI"),
]),
]And import it:
import DynamicUIimport SwiftUI
import DynamicUI
struct ContentView: View {
let json = """
[
{
"type": "Text",
"title": "Wait, am i generating views from JSON?",
"modifiers": {"foregroundStyle":"red","opacity":0.6}
},
{
"type": "Button",
"title": "Click me",
"eventHandler": "customHandler"
},
{
"type": "Toggle",
"title": "Toggle me",
"identifier": "my.toggle.1"
}
]
"""
@State private var component: DynamicUIComponent?
@State private var error: Error?
var body: some View {
DynamicUI(
json: json,
component: $component,
error: $error
)
}
}Conditional expressions can select a string value using another component's current state:
[
{
"type": "Toggle",
"title": "Toggle me",
"identifier": "myIdentifier"
},
{
"type": "Label",
"title": "Shine",
"url": "{$myIdentifier ? star.fill : star}"
}
]The syntax is {$identifier ? valueWhenTrue : valueWhenFalse}. It works in string-valued
component fields, including title, text, url, modifiers, and parameters.
The component binding receives the latest interacted-with component. Stateful controls include
their new value in state; identifier and eventHandler let your application route the update.
.onChange(of: component) { component in
guard let component else { return }
print(component.identifier as Any)
print(component.eventHandler as Any)
print(component.state as Any)
}You can use a callback instead of a binding:
import SwiftUI
import DynamicUI
struct ContentView: View {
let json = """
[
{
"type": "Text",
"title": "Wait, am i generating views from JSON?",
"modifiers": {"foregroundStyle":"red","opacity":0.6}
},
{
"type": "Button",
"title": "Click me",
"eventHandler": "customHandler"
},
{
"type": "Toggle",
"title": "Toggle me",
"identifier": "my.toggle.1"
}
]
"""
@State private var error: Error?
var body: some View {
DynamicUI(
json: json,
callback: { component in
// This contains everything passed as a component (type, title, identifier, ...)
print(component)
},
error: $error
)
}
}Every component requires a case-sensitive type. Common optional fields are:
| Field | Purpose |
|---|---|
title |
Label, title, placeholder, or accessibility label |
identifier |
Stable key for updates and conditional expressions |
eventHandler |
Application-defined event name returned on interaction |
defaultValue |
Initial value for stateful controls |
children |
Nested component array for containers |
url |
SF Symbol name for images, labels, and tab labels |
disabled |
Disables the component |
modifiers |
Visual and behavioral modifiers |
minimumValue, maximumValue |
Numeric bounds for sliders and progress views |
Unknown component types render no view. Decode failures are written to the optional error
binding and display a fallback error view.
The Playground directory contains an Xcode project with basic and exhaustive JSON examples. It
is available for macOS, iOS, watchOS, tvOS, and visionOS.
DynamicUI supports content views, controls, layouts, containers, navigation, tabs, and split views. See the complete schema, platform behavior, component examples, and modifier reference in the documentation.
- Aurora Editor for custom views in extensions.
π¦ @0xWDG π mastodon.social/@0xWDG π¦ @0xWDG π§΅ @0xWDG π wesleydegroot.nl π€ Discord