DEV Community

matsuji
matsuji

Posted on

Tips | Effortlessly Presenting UIViewController in SwiftUI

This article introduces tips for presenting UIViewController in SwiftUI.
While this might seem trivial, I’ll share the technique as there are no articles currently explaining it.

A Common Pattern Using UIViewController in SwiftUI

In general, UIViewControllerRepresentable is used when UIViewController is needed in SwiftUI.

struct FooView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> FooViewController {
        FooViewController()
    }

    func updateUIViewController(_ uiViewController: FooViewController, context: Context) {
        // Update the view state
    }
}
Enter fullscreen mode Exit fullscreen mode

This is a straightforward implementation, but it's a bit cumbersome to define a new subtype of UIViewControllerRepresentable for each UIViewController.
And it's also a bit cumbersome for me to name the subtype.

Use Generics

To solve the issue, define a generic type.
This is an implementation example.
The code is a bit long, but closures corresponding to each function of UIViewControllerRepresentable are just injected.

public struct UIViewControllerRepresenter<ViewController: UIViewController, Coordinator>: UIViewControllerRepresentable {
    private let makeCoordinatorHandler: @MainActor () -> Coordinator
    private let makeUIViewControllerHandler: @MainActor (Context) -> ViewController
    private let updateUIViewControllerHandler: @MainActor (ViewController, Context) -> Void
    private let sizeThatFitsHandler: @MainActor (ProposedViewSize, ViewController, Context) -> CGSize?

    public init(
        makeCoordinator: @escaping @MainActor () -> Coordinator = { () },
        makeUIViewController: @escaping @MainActor (Context) -> ViewController,
        updateUIViewController: @escaping @MainActor (ViewController, Context) -> Void = { _, _ in },
        sizeThatFits: @escaping @MainActor (ProposedViewSize, ViewController, Context) -> CGSize? = { _, _, _ in nil }
    ) {
        self.makeCoordinatorHandler = makeCoordinator
        self.makeUIViewControllerHandler = makeUIViewController
        self.updateUIViewControllerHandler = updateUIViewController
        self.sizeThatFitsHandler = sizeThatFits
    }

    public func makeCoordinator() -> Coordinator {
        makeCoordinatorHandler()
    }

    public func makeUIViewController(context: Context) -> ViewController {
        makeUIViewControllerHandler(context)
    }

    public func updateUIViewController(_ viewController: ViewController, context: Context) {
        updateUIViewControllerHandler(viewController, context)
    }

    @MainActor
    public func sizeThatFits(_ proposal: ProposedViewSize, uiViewController: ViewController, context: Context) -> CGSize? {
        sizeThatFitsHandler(proposal, uiViewController, context)
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage

Just use the type when you want to present UIViewController in SwiftUI.
You don't need to define new types.

struct BarView: View {
    var body: some View {
        UIViewControllerRepresenter { _ in
            FooViewController()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In a case where a coordinator is required or you need to do something on update, closures for them can be injected.

struct BarView: View {
    var body: some View {
        UIViewControllerRepresenter {
            FooCoordinator()
        } makeUIViewController: { context in
            let viewController = FooViewController()
            viewController.delegate = context.coordinator
            return viewController
        } updateUIViewController: { _, _ in
            print("updated")
        }
    }
}

class FooCoordinator: FooDelegate {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Note

This way is just for omitting of type definition and it doesn't cover all use cases.
For example, since the dismantleUIViewController function in UIViewControllerRepresentable is static, this method doesn’t cover cases where that function is needed.

Wrap up

In this article, I introduced a technique for effortlessly presenting UIViewController in SwiftUI.
This approach doesn’t cover all use cases, but it’s a powerful utility.
I hope this article can help your development!

Top comments (0)