10

I have this array of dictionaries.

let deckColors = [
    ["name": "blue", "desc": "desc1"],
    ["name": "yellow", "desc": "desc2"],
]

And my view:

struct ContentView: View {
    var body: some View {
        ForEach(0 ..< deckColors.count) {value in
            Text(deckColors[value]["name"])
        }
    }
}

How can I make it work? Currently getting this error: Protocol type Any cannot conform to StringProtocol because only concrete types can conform to protocols

Praveenkumar
  • 24,084
  • 23
  • 95
  • 173
user2641891
  • 143
  • 1
  • 1
  • 7

3 Answers3

10

This seems a very complex way to implement a struct:

struct DeckColor {
    var name: String
    var desc: String
}

let deckColors = [
    DeckColor(name: "blue", desc: "desc1"),
    DeckColor(name: "yellow", desc: "desc2")
]

struct ContentView: View {
    var body: some View {
        ForEach(0 ..< deckColors.count) { value in
            Text(deckColors[value].name)
        }
    }
}

The way you've implemented it requires dealing with the case that the dictionary does not include a "name" value. You can do that, but it's uglier and more fragile:

struct ContentView: View {
    var body: some View {
        ForEach(0 ..< deckColors.count) { value in
            Text(deckColors[value]["name"] ?? "default text if name isn't there.")
        }
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
5

This works for me

struct ContentView: View {
    let deckColors = [
        ["name": "blue", "desc": "desc1"],
        ["name": "yellow", "desc": "desc2"],
    ]

    var body: some View {
        ForEach(0 ..< deckColors.count, id: \.self) {value in
            Text(String(self.deckColors[value]["name"]!))
        }
    }
}
Mahmud Ahsan
  • 1,755
  • 19
  • 18
1

I would put your deck colors into an enum and then iterate on that.

enum DeckColor: String, CaseIterable {
    case blue
    case yellow

    var desc: String {
        switch self {
            case .blue: return "desc1"
            case .yellow: return "desc2"
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            ForEach(DeckColor.allCases, id: \.self) { color in
                Text(color.rawValue)
            }
        }
    }
}
  • This would result in displaying the description instead of the color's name – Leo Dabus Feb 17 '20 at 03:54
  • I prefer what Rob Napier suggested. But anyways with this approach you would need to declare your enumeration as `enum DeckColor: String, CaseIterable {` and pass the color rawValue to the Text initializer. `Text(color.rawValue)` – Leo Dabus Feb 17 '20 at 04:01
  • The reason I suggested an enum over a struct is that with a struct, you have to run initialiser code every time your app runs, which gets more and more cumbersome the more ‘deckColors’ there are. If you want to access them throughout your app, you then either have to create them every time, or create a singleton of them. Enum’s give you public access, are static, and give you type-safety. – tempest_fiend Feb 17 '20 at 18:48
  • It all depends if OP will need to change them or not. If you have an enumeration this would not be possible. – Leo Dabus Feb 17 '20 at 18:49
  • The use of a dictionary from OP indicates that they might be hard-coding all the values in, but you make a very valid point. – tempest_fiend Feb 18 '20 at 03:54
  • I get this error when running your code: `Cannot convert value of type 'Text' to closure result type 'Void'` – user2641891 Feb 18 '20 at 16:55
  • Was on mobile and totally blew past the fact it was for SwifUI.. Have updated :) – tempest_fiend Feb 18 '20 at 19:15