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

Time:02-08

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: ?????
}

CodePudding user response:

If for example Doctor and Patient conforms to protocol Human, then it could be as follows

protocol Human {
    var name: String { get set }

    // other code here
}

class Doctor: NSManagedObject, Human {
    var name: String = ""

    // other code here
}

struct GenericView<T: NSManagedObject & Human>: View {
    let items: SectionedFetchResults<String, T>

    var body: some View {
    // other code here
    }
}

CodePudding user response:

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)
})
  •  Tags:  
  • Related