Generating Forms in SwiftUI - Part 2

In the previous post I mentioned I’ve been using a method for generating SwiftUI forms dynamically. In this post I’ll explain how I do so.

My forms are defined in JSON and stored in my project’s FileWrapper. That is,the forms are not defined in SwiftUI in the Xcode project. This lets me decouple the presentation of the form from the definition of the form so that if I decide to change the data or the form, my core app will still be able to function.

Here is an example of my JSON form metadata, for a single form. This is metadata since it doesn't have the actual values to be displayed, only defaults.

JSON structure


The JSON file has two top level entities: Items, and Values. Items are required, Values, are optional depending on the item.

Each item in Items defines a SwiftUI widget to be displayed on the form, and the items are in the order of display, top to bottom.

Each item consists of the following attributes:

  • "label" : The label on the form for the widget,
  • "kind" : The name of the widget to display,
  • "default" : A default value to be displayed
  • "key" : The key in the data model to which this widget maps
  • "tag" : Optional. If kind is “picker”, this is the index of the Values item which contains an array of values for the picker.

I’ve made three simplifying assumptions when creating this structure:

  • The form has no nesting, it’s a flat structure.
  • All values returned and displayed by the form are of type String.
  • I use string names for widgets to make it readable and to allow me to create my own widgets that are composites of others.

In order to display the form, I read the JSON from my FileWrapper, decode it into Swift array of type [[[String:String]]]. This is then passed to my SwiftUI view that generates the actual form. Here is a simplified example of the form generator view.

Source code


This form can be expanded with additional widgets, and the code could be simplified, but I’m showing you an example I hope is understandable. I’ve omitted a lot of error checking and some other widgets I have.

The results dictionary is preloaded (outside this view) with the current model values. On exit from this view, results has the data entered from the form, and is used to load back into the model.

That’s it. It may not be an optimal solution, but it works for what I need.

blog comments powered by Disqus