Accessing UIHostingController from a SwiftUI view

While I was working on a mixed UIKit and SwiftUI project, I needed a way to access the UIHostingController within the SwiftUI view so that I could use it for interacting with other UIKit methods. This blog post tackles the problem and provides a simple solution how to implement it.

The approach we are taking is using the SwiftUI environment and inserting an object into the environment, which then keeps a weak reference to the view controller hosting the SwiftUI view. Using the SwiftUI view environment has a benefit of allowing multiple other SwiftUI views within the hierarchy to use it as well. In the end, we would like to write something like this:

// Presenting the detail view using UIKit presentation methods
let hostingController = DetailView().embeddedInHostingController()
presentingViewController.present(hostingController, animated: true)
// The view which needs access to the view controller hosting it
struct DetailView: View {
@EnvironmentObject var hostingProvider: ViewControllerProvider
var body: some View {
VStack {
Text("Detail")
Button("Access View Controller") {
let viewController = hostingProvider.viewController
// … do something with the view controller
}
}
}
}

In the snippet above, we use a custom embeddedInHostingController() function which inserts a new ViewControllerProvider type the to the environment. Let’s take a closer look how this function and type are implemented.

extension View {
func embeddedInHostingController() -> UIHostingController<some View> {
let provider = ViewControllerProvider()
let hostingAccessingView = environmentObject(provider)
let hostingController = UIHostingController(rootView: hostingAccessingView)
provider.viewController = hostingController
return hostingController
}
}
final class ViewControllerProvider: ObservableObject {
fileprivate(set) weak var viewController: UIViewController?
}

The ViewControllerProvider class keeps a weak reference to the view controller. Since UIHostingController is a subclass of UIViewController we can just use UIViewController as a type. The embedded function creates an instance of the provider and a hosting controller, inserts the provider into the SwiftUI view environment and then sets the weak property which we can access later.

If this was helpful, please let me know on Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.