Search code examples
iosswiftswiftuiuihostingcontroller

How to generically cast a UIHostingController from a UIViewController without knowing the rootView


I have a custom UINavigationController which performs some pushing and popping actions.

I using SwiftUI within a UIKit application so a few of the view controllers within the UINavigationController are UIHostingControllers

In various parts of my code I have UIHostingController(rootView: ArticleView()) or UIHostingController(rootView: RatingView()) and some others

I wanted to specifically identify if the Navigation controller was going to push the article view or the rating view so what I did was:

protocol TabBarCompatible: View { }

extension ArticleView: TabBarCompatible { }

extension RatingView: TabBarCompatible { }

Then when I am going through the view controllers in my Navigation controller, I check to see if the destination is like one of the above:

if let destinationView = self as? UIHostingController<AnyView>,
   destinationView.rootView is any TabBarCompatible

Now the cast to UIHostingController<AnyView> itself fails which probably could succeed if when I created the SwiftUI views, I would need to do UIHostingController(rootView: AnyView(ArticleView())) - however:

  • What I really wish to check is `if let destinationView = self as? UIHostingController
  • I prefer not to wrap to AnyView unless its the last resort as I will have to do this in many places

Another option could also be to hold all the possibilities I wish to check in an array and keep

let swiftUIControllers = [UIHostingController<ArticleView>, UIHostingController<RatingView>]

// loop through the swiftUIControllers and compare the current controller using isKind(of: )

Is there any other / better way to cast a UIViewController to a UIHostingController generically without knowing the rootView of the UIHostingController ?


Solution

  • If you just want to check if the rootView conforms to TabBarCompatible and don't care about anything else about rootView, you can introduce a new protocol like this:

    protocol TabBarCompatibleViewController: UIViewController {}
    
    extension UIHostingController: TabBarCompatibleViewController where Content: TabBarCompatible {}
    

    Then you can check if destination is (any TabBarCompatibleViewController).

    If you remove the : View constraint on TabBarCompatible, you can reuse TabBarCompatible instead of writing a new protocol,

    extension UIHostingController: TabBarCompatible where Content: TabBarCompatible {}