Display SwiftUI views from Objective-C codebase
Tags: ios, swift, swiftui, objc
Published: 2021-11-18 19:20:14

---
title: Display SwiftUI views from Objective-C codebase
published: true
date: 2021-11-18 18:44:55 UTC
tags: ["ios", "swift", "swiftui", "objc"]
canonical_url: https://tarikdahic.com/posts/display-swiftui-views-from-objective-c-codebase/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btfno0qq1gofny7fovcr.jpg
---

 **SwiftUI** is getting more mature with every new major iOS release and it is now in a state where it can be used for a lot of use cases. The app that I work on my daily job is based on Objective-C and I wanted to prototype some things in SwiftUI because it was **faster** and **more fun**. It was also a great way to escape **UIKit** for a little and learn more about SwiftUI. That means I had to display SwiftUI views from Objective-C ViewControllers. In this article I will explain how I did that.

There are some limitations between Objective-C and Swift regarding SwiftUI. SwiftUI views are structs and they are not visible from the Objective-C code. The only way to bridge the two worlds is to use some kind of a mediator class between Objective-C and Swift code.

## SwiftUI and UIKit

Because we will be displaying SwiftUI views from UIKit we first need to know what abstractions to use to wrap SwiftUI views. UIKit can only present or push `UIViewController` instances. To create `UIViewController` instance from SwiftUI view we need to wrap it into a `UIHostingController`. In `UIHostingController` initializer we pass the SwiftUI view that we want to wrap like in the example below:

```swift
let controller = UIHostingController(rootView: SwiftUIView())
```

`UIHostingController` is a subclass of `UIViewController` and now `controller` variable can be used from our UIKit code to present it, push it onto the navigation stack or add it as a child view controller. You can read more about `UIHostingController` [here](https://developer.apple.com/documentation/swiftui/uihostingcontroller).

## Adding Obj-C to the mix

Now that we know how to display SwiftUI views inside UIKit we need to bridge `UIHostingController` to the Objective-C world. As I mentioned earlier, Swift structs are not visible from the Objective-C context so we need to add a middleman that will create `UIHostingController` instances wrapped around SwiftUI views.

> I like to call the middleman implementations as SwiftUIViewAdapter or SwiftUIViewFactory. I am still not settled on a final suffix but something between these two seems right.

Let’s start with a simple SwiftUI view that will display the text that is passed into its initializer:

```swift
import SwiftUI

struct HelloWorldView: View {
  
    var text: String
    
    init(text: String) {
        self.text = text
    }
    
    var body: some View {
        Text(text)
    }
}
```

To display the `HelloWorldView` we need to wrap it into the middleman class that will create the `UIHostingController`:

```swift
@objc class HelloWorldViewFactory: NSObject {
    
    @objc static func create(text: String) -> UIViewController {
        let helloWorldView = HelloWorldView(text: text)
        let hostingController = UIHostingController(rootView: helloWorldView)
        
        return hostingController
    }
}
```

There are a couple of things happening in the `HelloWorldViewFactory` class implementation above, so let’s cover them one by one:

- `HelloWorldViewFactory` class has one static function `create(text:)` that will create the `HelloWorldView` and initialize it, wrap it inside the `UIHostingController` and return it.
- To expose `HelloWorldViewFactory` to Objective-C code we need to prefix it with `@objc` annotation and we need to subclass `NSObject`. This is because `NSObject` is the root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.
- You can also notice that we are passing _text_ String argument to `create` function. This allows us to use custom initializers for SwiftUI views and to pass dependencies that views need.

Now we can display this view from Objective-C like this:

```obj-c
UIViewController *vc = [HelloWorldViewFactory createWithText:@"Hello from Obj-C!"];
[self presentViewController:vc animated:YES completion:nil];
```

In this example the device should display the view that has text centered and set to _Hello from Obj-C!_

 ![](https://tarikdahic.com/images/swiftui-from-objc-mockup.png)

#### Importing Swift code into Objective-C

If you didn’t use Swift in your existing Objective-C project it is important to know that you need to import the Swift module header into the Objective-C file that will use your Swift code. That header file is named like `$(SWIFT_MODULE_NAME)-Swift.h` and if you want, you change the name for that header file inside build settings for your target. The config option that defines the name is _Objective-C Generated Interface Header Name_.

If your app target is called `MyTestApp` you can import the Swift code into Objective-C like this:

```obj-c
#import "MyTestApp-Swift.h"
```

If you are in the same situation as I was I hope that this article will help you.