leonboe1
leonboe1

Reputation: 1185

SwiftUI allow all string enums in Struct

It's probably a stupid question but I'm trying to allow all string enums as type for a variable in a Struct. The code below is completely wrong but I hope that it will make my question clearer.

My idea was to conform all enums to the same protocol but I can't access .allCases with this approach.

My goal is that I can pass any string enum to the ListView which will then display all components of the enum (here: one; two; three).

Does anybody have an idea how to do this? It must be a very basic Swift thing but I wasn't able to figure it out searching through the web. Thank you so much!

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        ListView(myEnum: Elements.self) //error here as well
    }
}

protocol StringRepresentable {
    var rawValue: String { get }
}

enum Elements: String, Equatable, CaseIterable, StringRepresentable {
    case one
    case two
    case three
}

struct ListView: View {
    let myEnum: StringRepresentable //doesn't work
    
    var body: some View {
        ForEach(myEnum.allCases, id: \.self) { elem in //can't access .allCases
            Text(elem.rawValue)
        }
    }
}

Upvotes: 2

Views: 850

Answers (2)

Claus Jørgensen
Claus Jørgensen

Reputation: 26347

There's several errors in your original code. First you weren't using a VStack (or List or LazyVStack) so your foreach would only render one element.

Secondly, you were passing the type, not the elements itself to the list view. And finally, your own StringRepresentable protocol is unnecessary, you can use RawRepresentable with it's associated type RawValue constrained to String

i.e. something like this:

struct ContentView: View {
    var body: some View {
        VStack {
            ListView(values: Fruits.allCases)
            ListView(values: Animals.allCases)
        }
    }
}

enum Fruits: String, CaseIterable {
    case apple
    case orange
    case banana
}

enum Animals: String, CaseIterable {
    case cat
    case dog
    case elephant
}

struct ListView<T: RawRepresentable & Hashable>: View where T.RawValue == String {
    let values: [T]

    var body: some View {
        LazyVStack {
            ForEach(values, id: \.self) { elem in
                Text(elem.rawValue)
            }
        }
    }
}

Which renders like this

render

Upvotes: 4

Joakim Danielson
Joakim Danielson

Reputation: 51892

Here is a variant that will work by sending in all items of an enum to the view rather that the enum itself and making the view generic.

struct ContentView: View {
    var body: some View {
        ListView(myEnum: Elements.allCases)
    }
}

protocol StringRepresentable {
    var rawValue: String { get }
}

enum Elements: String, Equatable, CaseIterable, StringRepresentable {
    case one
    case two
    case three
}

struct ListView<T: CaseIterable & StringRepresentable>: View {
    let myEnum: [T]
    @State private var selectedValue: String = ""

    var body: some View {
        ForEach(0..<myEnum.count) { index in
            Text(myEnum[index].rawValue)
                .onTapGesture {
                    selectedValue = myEnum[index].rawValue
                }
        }
    }
}

Upvotes: 1

Related Questions