Home > Enterprise >  How to return generic class with opaque type
How to return generic class with opaque type

Time:11-16

How can I return opaque type for generic class?

func makeFAQController() -> UIHostingController<some View> {
    let viewModel = FAQViewModel()
    let view = FAQView().environmentObject(viewModel)
    let controller = UIHostingController(rootView: view)
    return controller
}

Error: Cannot convert value of type 'UIHostingController<some View>' to type 'UIHostingController<FAQView>' in coercion

func makeFAQController() -> UIHostingController<FAQView> {
    let viewModel = FAQViewModel()
    let view = FAQView().environmentObject(viewModel)
    let controller = UIHostingController(rootView: view)
    return controller
}

Cannot convert value of type 'UIHostingController<some View>' to type 'UIHostingController<FAQView>' in coercion

I want to avoid type erasure if possible.

CodePudding user response:

You should return base class (there is nothing more generic then base class, probably only meta NSObject), so

func makeFAQController() -> UIViewController {    // << here !!
    let viewModel = FAQViewModel()
    let view = FAQView().environmentObject(viewModel)
    let controller = UIHostingController(rootView: view)
    return controller
}

CodePudding user response:

You might as well use type-erased AnyView here:

func makeFAQController() -> UIHostingController<AnyView> {
    let viewModel = FAQViewModel()
    let view = FAQView().environmentObject(viewModel)
    let controller = UIHostingController(rootView: AnyView(view))

    return controller
}

Another approach avoiding AnyView would be to make the enclosing class of makeFaqController method generic and store in it a closure/viewBuilder to use as view to host at creation time:

class FAQMaker<U: View> {
    fileprivate let _prepare: (FAQView) -> U
    init(_ prepare: @escaping (FAQView) -> U) {
        self._prepare = prepare
    }
    
    func makeFAQController() -> UIHostingController<U> {
        let prepared = _prapare(FAQView())
        return UIHostingController(rootView: prepared)
    }

}

The former approach worked for me fine to wrap SwiftUI views into UICollectionReusableView.

  • Related