| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Render server-driven SwiftUI interfaces from JSON across Apple platforms.
DynamicUI turns versioned JSON component trees into native SwiftUI views. Use it for remote forms, configurable extension interfaces, feature-driven layouts, and rapid prototypes without giving up native controls or accessibility semantics.
Add DynamicUI using Swift Package Manager:
dependencies: [
.package(url: "https://github.com/0xWDG/DynamicUI.git", exact: "0.1.1"),
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "DynamicUI", package: "DynamicUI"),
]),
]In Xcode, select File → Add Package Dependencies, enter https://github.com/0xWDG/DynamicUI, and choose version 0.1.1.
Then import the package:
import DynamicUIimport SwiftUI
import DynamicUI
struct ContentView: View {
let json = """
[
{
"type": "Text",
"title": "This interface comes from JSON",
"modifiers": {"foregroundStyle":"red","opacity":0.6}
},
{
"type": "Button",
"title": "Continue",
"eventHandler": "continue"
},
{
"type": "Toggle",
"title": "Show details",
"identifier": "showDetails"
}
]
"""
@State private var component: DynamicUIComponent?
@State private var error: Error?
var body: some View {
DynamicUI(json: json, component: $component, error: $error)
}
}For production payloads, use the versioned layout envelope:
{
"schemaVersion": 1,
"components": [
{ "type": "Text", "title": "A validated layout" }
]
}Legacy top-level component arrays remain supported. Validate either representation before rendering with try DynamicUILayout(json: json). Validation rejects unsupported schema versions, empty types and identifiers, duplicate identifiers, and malformed component conditions.
Conditional expressions can select strings or control whether an entire component renders:
[
{
"type": "Toggle",
"title": "Show favorite",
"identifier": "favorite"
},
{
"type": "Label",
"title": "{$favorite ? Saved : Not saved}",
"url": "{$favorite ? star.fill : star}"
},
{
"type": "Text",
"title": "The favorite is enabled",
"if": "$favorite"
}
]The string syntax is {$identifier ? valueWhenTrue : valueWhenFalse}. Missing identifiers and empty, zero, false, or null values select the false branch and hide conditional views.
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:
DynamicUI(json: json) { component in
print(component.eventHandler as Any, component.state as Any)
}Render application-specific component types without forking DynamicUI. Return nil for component types your renderer does not recognize:
DynamicUI(
json: json,
component: $component,
error: $error,
customViewRenderer: { component in
guard component.type == "ProductCard" else { return nil }
return AnyView(
ProductCard(
title: component.title ?? "Product",
productID: component.parameters?["productID"]?.toString()
)
)
}
)DynamicUI applies supported modifiers and accessibility metadata to the custom view. Unknown types that neither DynamicUI nor the application recognizes are logged and skipped.
DynamicUI uses native SwiftUI controls and supports explicit assistive-technology metadata:
{
"type": "Button",
"title": "Save",
"accessibilityLabel": "Save profile",
"accessibilityHint": "Saves your profile changes",
"accessibilityValue": "Ready",
"accessibilityIdentifier": "profile.save",
"accessibilityInputLabels": ["Save", "Save profile"]
}Use accessibilityHidden: true only for decorative content. Visible control titles continue to provide native semantics when explicit accessibility metadata is not needed. Accessibility string fields support the same conditional expressions as visible strings.
Every component requires a case-sensitive type. Common optional fields are:
| Field | Purpose |
|---|---|
| title | Label, title, placeholder, or image description |
| if | Identifier condition such as $showDetails that controls rendering |
| 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 or URL, depending on the component |
| disabled | Disables the component |
| modifiers | Visual and behavioral modifiers |
| minimumValue, maximumValue | Numeric bounds for sliders and progress views |
| accessibilityLabel | Concise, speakable name for assistive technologies |
| accessibilityHint | Describes the result of interacting with the component |
| accessibilityValue | Accessible state or formatted value |
| accessibilityIdentifier | Stable identifier for UI automation |
| accessibilityHidden | Hides decorative content from assistive technologies |
| accessibilityInputLabels | Alternative spoken names for Voice Control |
Unknown component types are logged and skipped, allowing valid sibling components to keep rendering. Decode and validation failures are written to the optional error binding and display a fallback error view. Objects without a string type field are treated as metadata and ignored in component arrays.
The Playground directory contains an Xcode project with basic and exhaustive JSON examples for macOS, iOS, watchOS, tvOS, and visionOS.
See the complete schema, platform behavior, component examples, and modifier reference in the documentation.
Using DynamicUI in your project? Open a pull request to add it here.
Issues and pull requests are welcome. See CONTRIBUTING.md for setup, testing, and contribution guidance, or browse the good first issue label.
🦋 @0xWDG 🐘 mastodon.social/@0xWDG 🐦 @0xWDG 🧵 @0xWDG 🌐 wesleydegroot.nl 🤖 Discord
| Back | FazBrowse Home | New Git URL |