3
\$\begingroup\$

I've written a simple list in SwiftUI that accepts a generic data type.

The goal was to make a reusable list where when an item was selected, the list would execute a callback with the selected data.

I struggled with it for a while and have my doubts so I'm keen to get feedback on my implementation.

struct ContentView: View { var body: some View { VStack { BottomSheetView<Test>(options: [Test(name: "hi", type: 7)], selectedOptionsCompletion: {value in}) BottomSheetView<Test2>(options: [Test2(name: "hi again", type: "7")], selectedOptionsCompletion: {value in}) } .padding() } } struct BottomSheetView<T: ListItem>: View { var options: [T] var selectedOptionsCompletion: ((T.DataType) -> Void)? var body: some View { VStack(alignment: .leading, spacing: 5) { ForEach(options) {item in Text(item.name).onTapGesture { let _ = print(type(of: item.type)) selectedOptionsCompletion!(item.type) } } } .presentationDetents([.medium, .fraction(0.15)]) .presentationDragIndicator(.hidden) .padding() } } protocol ListItem: Identifiable { var id: UUID {get} var name: String { get set} associatedtype DataType var type: DataType { get set} } struct Test: ListItem { var id: UUID = UUID() var name: String var type: Int init(name: String, type: Int) { self.name = name self.type = type } } struct Test2: ListItem { var id: UUID = UUID() var name: String var type: String init(name: String, type: String) { self.name = name self.type = type } } 
\$\endgroup\$

    1 Answer 1

    3
    \$\begingroup\$

    It's fine as far as it goes:

    • The name and type in ListItem should not be get set. They should only be get.
    • The properties in the BottomSheetView should be let not var.
    • The name is problematic... How is this a "bottom sheet"?
    • I think you will find that there will be so many subtle differences between the various list views in your app that trying to make a single reusable one will be problematic.

    It's not very flexible though because all of the types that you want to display in a list will have to conform to the ListItem protocol. I would rather T in the BottomSheetView just be required to conform to Identifiable. You can pass in a closure (T) -> String that produces the name of the object.

    So something like this:

    struct BottomSheetView<T: Identifiable>: View { let options: [T] let name: (T) -> String let selectedOptionsCompletion: (T) -> Void init(options: [T], name: @escaping (T) -> String = { String(describing: $0) }, selectedOptionsCompletion: @escaping (T) -> Void) { self.options = options self.name = name self.selectedOptionsCompletion = selectedOptionsCompletion } var body: some View { VStack(alignment: .leading, spacing: 5) { ForEach(options) { item in Text(name(item)).onTapGesture { selectedOptionsCompletion(item) } } } .presentationDetents([.medium, .fraction(0.15)]) .presentationDragIndicator(.hidden) .padding() } } 
    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.