Search code examples
swiftgenericscore-dataswiftui

How to define a generic SwiftUI view that can accept sectioned fetch results for different entities


I need to define a generic SwiftUI view that can accept sectioned fetch results for different CoreData entities, but I'm not sure how to define the generic view.

In the example below, I have two sectioned fetch results defined for Patient and Doctor entities. I need to be able to pass them to the generic view.

@SectionedFetchRequest(
    sectionIdentifier: \.sectionTitle,
    sortDescriptors: Patient.nameSortDescriptors(), animation: .default)
private var patients: SectionedFetchResults<String, Patient>

@SectionedFetchRequest(
    sectionIdentifier: \.sectionTitle,
    sortDescriptors: Doctor.nameSortDescriptors(), animation: .default)
private var doctors: SectionedFetchResults<String, Doctor>

GenericView(items: patients)
GenericView(items: doctors)

struct GenericView: View {
    let items: ?????
}

Solution

  • One way is to supply not only the fetched results but also the view to use for each object in the results.

    The below view is generic for the objects to display, Object, and the view to use, Content, for each Object. In this example I am displaying a list of all objects

    struct GenericView<Object: NSManagedObject, Content: View>: View {
        let items: SectionedFetchResults<String, Object>
        let rowContent: (Object) -> Content
    
        init(items: SectionedFetchResults<String, Object>, rowContent: @escaping (Object) -> Content) {
            self.items = items
            self.rowContent = rowContent
        }
        
        var body: some View {
            List {
                ForEach(items) { section in
                    Section(header: Text(section.id)) {
                        ForEach(section, id: \.objectID) { item in
                            rowContent(item)
                        }
                    }
                }
            }
        }
    }
    

    Then you call the view like this for eaxample

    GenericView(items: patients, rowContent: { patient in
        Text(patient.name)
    })