简体   繁体   中英

Use string path to access struct variables in Swift

I am building a custom renderer for JSON Forms in Swift to integrate with the JSON Forms architecture being used for our web-based apps. If you aren't familiar, two JSON files are used to generate UI form components and link to the back end. Below are an example of the JSON Schema and the JSON UI Schema as well as the Person struct I am generating from the JSON Schema.

The scope value provides a string that can be used to link to the variables for the backend person data, but I am having a hard time figuring how to do so in a scalable way. The backend will be a combination of Core Data and (probably) SQLite (with SQLite.swift), but the struct shown would be the object from which the data will be pulled. I was hoping Keypaths would be the way, but I cannot figure out how to generate a Keypath directly from a string. I suppose I could use reference dictionaries (String: Keypath) but that would likely get out of hand with the backend we will likely be using.

Before you ask "why even do this?"the answer is that is the mandate handed down to me by the brass, so... I gotta!

JSON UI Schema:

{
  "type": "VerticalLayout",
  "elements": [
    {
      "type": "HorizontalLayout",
      "elements": [
        {
          "type": "Control",
          "scope": "#/properties/firstName"
        },
        {
          "type": "Control",
          "scope": "#/properties/lastName"
        }
      ]
    },
    {
      "type": "HorizontalLayout",
      "elements": [
        {
          "type": "Control",
          "scope": "#/properties/age"
        },
        {
          "type": "Control",
          "scope": "#/properties/dateOfBirth"
        }
      ]
    },
    {
      "type": "HorizontalLayout",
      "elements": [
        {
          "type": "Control",
          "scope": "#/properties/height"
        },
        {
          "type": "Control",
          "scope": "#/properties/gender"
        },
        {
          "type": "Control",
          "scope": "#/properties/committer"
        }
      ]
    },
    {
      "type": "Group",
      "label": "Address for Shipping T-Shirt",
      "elements": [
        {
          "type": "HorizontalLayout",
          "elements": [
            {
              "type": "Control",
              "scope": "#/properties/address/properties/street"
            },
            {
              "type": "Control",
              "scope": "#/properties/address/properties/streetnumber"
            }
          ]
        },
        {
          "type": "HorizontalLayout",
          "elements": [
            {
              "type": "Control",
              "scope": "#/properties/address/properties/postalCode"
            },
            {
              "type": "Control",
              "scope": "#/properties/address/properties/city"
            }
          ]
        }
      ],
      "rule": {
        "effect": "ENABLE",
        "condition": {
          "scope": "#/properties/committer",
          "schema": {
            "const": true
          }
        }
      }
    }
  ]
}

JSON Schema:

{
  "type": "person",
  "required": [
    "age"
  ],
  "properties": {
    "firstName": {
      "type": "string",
      "minLength": 2,
      "maxLength": 20
    },
    "lastName": {
      "type": "string",
      "minLength": 5,
      "maxLength": 15
    },
    "age": {
      "type": "integer",
      "minimum": 18,
      "maximum": 100
    },
    "gender": {
      "type": "string",
      "enum": [
        "Male",
        "Female",
        "Undisclosed"
      ]
    },
    "height": {
      "type": "number"
    },
    "dateOfBirth": {
      "type": "string",
      "format": "date"
    },
    "rating": {
      "type": "integer"
    },
    "committer": {
      "type": "boolean"
    },
    "address": {
      "type": "object",
      "properties": {
        "street": {
          "type": "string"
        },
        "streetnumber": {
          "type": "string"
        },
        "postalCode": {
          "type": "string"
        },
        "city": {
          "type": "string"
        }
      }
    }
  }
}

Person struct:

struct Person: Storable {
    
    var uuid: UUID = UUID()
    var firstName: String?
    var lastName: String?
    var age: Int?
    var gender: String?
    var dateOfBirth: Date?
    var height: Double?
    var rating: Int?
    var committer: Bool?
    var address: Address?
}

struct Address: Storable {
   
    var street: String?
    var streetNumber: String?
    var postalCode: String?
    var city: String?
}

I looked into this. Swift Keypaths are defined at compile-time, and you can't define them with runtime strings.

The older KVO-based keypath mechanism for Objective-C objects CAN specify keypaths using strings, but those are not type-safe, and you need to be a lot more defensive when using them.

There is nothing in the language to support this if they aren't Objective-C properties, so you will ultimately need a mapping of strings to keypaths.

Since it's basically boilerplate code, you could use Sourcery to generate the mapping, or even use something that can parse the JSON schema to generate it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM