0

How can I get the value of a struct type by mean of a string?

func getField(action: myStruct, field:String) {
    labelAmount.text = action[field]
}

This is my struct:

struct myStruct {
    var id: Int
    var name: String
    .
    .
    //many fields
}

I get this error:

Value of type 'myStruct' has no subscripts
becauseR
  • 39
  • 7
  • 1
    `action.id` will give you the value – Joakim Danielson Oct 30 '19 at 16:58
  • @JoakimDanielson i have many fields, because i want find by mean field:String – becauseR Oct 30 '19 at 17:02
  • To clarify, you want a function that can return any value in the struct based on the `field` paramter? So each time it can return a different value. – flanker Oct 30 '19 at 17:03
  • @becauseR What you're looking for is a feature called "reflection". Swift has the internal metadata to make possible, but no real APIs to surface reflection capabilities to programmers. `Mirror` might be sufficient for your usecase, but it's probably best to try to come up with a design that doesn't need reflection. If you really need it, you can take a look at third party Swift reflection Libraries. – Alexander Oct 30 '19 at 17:04
  • 2
    Consider using a [KeyPath](https://stackoverflow.com/q/44447032/1630618) – vacawama Oct 30 '19 at 17:13
  • 1
    Would it be possible to change your code to use a Dictionary instead of a custom struct? That seems like the most logical way of handling this to me. – John Montgomery Oct 30 '19 at 17:14

2 Answers2

1

Like already mentioned in the comments you can use reflection for this

func getField(action: myStruct, field:String) {
    let actionMirror = Mirror(reflecting: action)
    if let value = actionMirror.children.first(where: {$0.label == field}).map( {$0.value}) {
        labelAmount.text = String(describing: value)
    }
}

Another solution suggested in the comments was using KeyPath

func getField<T>(action: myStruct, path: KeyPath<myStruct, T>) {
    labelAmount.text = String(describing: action[keyPath: path])
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
0

Use an enum within the struct to define which field to return:

struct MyStruct{
  enum Field: String {
    case id
    case name
    // and any other fields
  }
    var id: Int
    var name: String
    .
    .
    //many fields

    func fieldAsString(for field: Field) -> String {
      switch field {
      case .id: return String(Int)
      case .name: return name
      //etc. for all other cases of Field enum
    }
}

then, for any instance of MyStruct, you can

var myStruct: MyStruct

label.text = myStruct.fieldAsString(for: .name)
flanker
  • 3,840
  • 1
  • 12
  • 20