How to embed a UIKit ViewController inside a SwiftUI View
Tags: beginners, ios, uikit, swiftui
Published: 2023-05-22 05:50:15

Hello community, the other day I was trying to use Xcode playground to test some UI but the experience was not the best. Xcode was crashing constantly so I decided to try another approach.

Use the SwiftUI preview to test my UIKit Views !.

So in this tutorial we're going to learn the first steps into integrating UIKit inside SwiftUI.

Let's start!

## Creating the project

First let's start Xcode and create a new project, then select iOS > App and click Next. This time select SwiftUI as the interface and give it any name you want. Finally select a place to save the files and create the project.

## Creating the UIKit View

Let's create a new swift file with the name of `CounterViewController.swift`. We are going to create our UIKit interface inside this file.

For simplicity, let's make a simple view that shows a label an a button.

```swift
import UIKit

final class CounterViewController: UIViewController {
    
    private var counter = 0
    
    private lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Tap me from UIKit", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    private lazy var label: UILabel = {
        let label = UILabel()
        label.text = getMessage()
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addComponents()
        addHandlers()
        addConstrains()
    }
    
}

private extension CounterViewController {
    func getMessage() -> String{
        "UIKit Button was tapped \(counter)"
    }
    
    func addConstrains() {
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: view.topAnchor, constant: 64),
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.widthAnchor.constraint(equalToConstant: 250),
            label.heightAnchor.constraint(equalToConstant: 50),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 64),
            button.heightAnchor.constraint(equalToConstant: 48),
            button.widthAnchor.constraint(equalToConstant: 200),
        ])
    }
    
    func addComponents() {
        [button, label]
            .forEach { view.addSubview($0) }
    }
    
    func addHandlers() {
        button.addTarget(self, action: #selector(didButtonTapped), for: .touchUpInside)
    }
    
    @objc func didButtonTapped(_ sender: UIButton) {
        counter += 1
        label.text = getMessage()
    }
    
}
```

the above code shows a Label that will be updated once the button is pressed.

## Creating the SwiftUI binding

To embed our UIKit code inside the SwiftUI we need to create a new file that contains a struct that conforms to `UIViewControllerRepresentable`. 

Create a new swift file called `CounterView` and add the following code:

```swift
import SwiftUI

struct CounterView: UIViewControllerRepresentable {
    
    typealias UIViewControllerType = CounterViewController
    
    func makeUIViewController(context: Context) -> CounterViewController {
        CounterViewController()
    }
    
    func updateUIViewController(_ uiViewController: CounterViewController, context: Context) {
       // Update the ViewController here
    }
}
```

This struct is the wrapper we are going to use to create our SwiftUI view.

To conform to `UIViewControllerRepresentable` our structs needs three things

1. `typealias UIViewControllerType`: This is a type alias for the UIKitClass, in our case is the ViewController.
2. `func makeUIViewController(context: Context)`: This functions is the one that will create an instance of our ViewController for SwiftUI
3. `func updateUIViewController(_ uiViewController: CounterViewController, context: Context)`: This function is called each time a change in our view state occurs, we can update our UIKit in this function.

## Creating our View object in SwiftIU

Finally, let's create our binding view that we will use in SwiftUI.

To do so just instantiate the new View object and that's all.

```swift
import SwiftUI

struct ContentView: View {
    
    var body: some View {
        VStack {
            Text("SwiftUI + UIKit")
                .padding(48)
                .font(.title.bold())
            CounterView(labelText: $label)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

Xcode live previewing the UIKit component, you can click it on the button and it will work!.

![swiftui-preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j21py07sgvjuceq92d1e.png)

Now you can make changes to your UIKit code and see a live update on the SwiftUI preview.

## Conclusion

In this first tutorial we were able to embed a UIKit view controller inside a SwiftUI View and we were able to preview our work inside the live preview. This first attempt show us how easy is to create interoperability between SwiftUI and UIKit. 

In coming tutorial, we will be adding bidirectional communication with `@Binding` and `@State`.

Thank you for reading.