i have a UIHostingController that is hosting a SwiftUI view called CatalogView. when showing it, an environment object is attached, so basically from UIKit it is shown like this:
let rootCatalogView = CatalogView()
let appState = AppState.get()
let catalogView = UIHostingController(rootView: rootCatalogView.environmentObject(appState))
navigationController.pushViewController(catalogView, animated: true)
now at a later time i need to check if this UIHostingController is in the list of navigationController.viewControllers
the type(of:) is showing the following, which kind of make sense:
UIHostingController<ModifiedContent<CatalogView, _EnvironmentKeyWritingModifier<Optional<AppState>>>>
things like vc.self is UIHostingController.Type or vc.self is UIHostingController< CatalogView >.Type both return false (vc is an element of navigationController.viewControllers
the following obviously works, it returns true, but any change in the initialisation of the UIHostingController will change its type
vc.isKind(of: UIHostingController<ModifiedContent<CatalogView, _EnvironmentKeyWritingModifier<Optional<StoreManager>>>>.self)
how can i check if the view controller is of type UIHostingController? or at least how can i cast the controller to UIHostingController so that i can check its rootview?
Due to the generic parameter, we cannot cast the ViewController to find if it is a UIHostingController
without knowing the full constraint.
UIHostingController
is a subclass of UIViewController
so we could do the following.
Create a computed property on UIViewController
that returns the name of the class that is used to create UIViewController
. This gives us something to search for in the list of ViewControllers
contained in the UINavigationController
extension UIViewController {
var className: String {
String(describing: Self.self)
}
}
Create a few UIViewController subclasses and our UIHostingController
class FirstViewController: UIViewController {}
class SecondViewController: UIViewController {}
class MyHostingController<Content>: UIHostingController<Content> where Content : View {}
let first = FirstViewController()
let second = SecondViewController()
let hosting = UIHostingController(rootView: Text("I'm in a hosting controller"))
let myHosting = MyHostingController(rootView: Text("My hosting vc"))
We can then add these to a UINavigationController
.
let nav = UINavigationController(rootViewController: first)
nav.pushViewController(second, animated: false)
nav.pushViewController(hosting, animated: false)
nav.pushViewController(myHosting, animated: false)
Now that we have some ViewControllers inside our UINavigationController we can now iterate across them and find a ViewController that has a className that contains what we are looking for.
for vc in nav.viewControllers {
print(vc.className)
}
This would print the following to the console:
FirstViewController
SecondViewController
UIHostingController<Text>
MyHostingController<Text>
You can then for-where
to find the ViewController in the hierarchy.
for vc in nav.viewControllers where vc.className.contains("UIHostingController") {
// code that should run if its class is UIHostingController
print(vc.className)
}
for vc in nav.viewControllers where vc.className.contains("MyHostingController") {
// code that should run if its class is MyHostingController
print(vc.className)
}
As I said above, this is not an ideal solution but it may help you until there is a a better way of casting without knowing the generic constraint.