Before the introduction of SwiftUI, all iOS apps were built using UIKit along with a collection of frameworks based on and supported by UIKit. Although SwiftUI offers a large collection of components for creating an app, there are cases where there is no SwiftUI equivalent to the options provided by other frameworks.
Due to the large number of apps developed before the introduction of SwiftUI, it is important to be able to integrate non-SwiftUI functionality with SwiftUI projects, and vice versa. Fortunately, SwiftUI includes many options for this type of integration.
Before going into the details of how to integrate SwiftUI and UIKit, it is worth taking some time to explore whether a new project should be started using UIKit or SwiftUI and whether an app should be fully migrated to SwiftUI. When making this decision, keep in mind that SwiftUI apps can only be used on devices running iOS 13 or higher.
If you’re starting a new project, it’s best to build your app in SwiftUI and integrate UIKit when the functionality isn’t directly provided by SwiftUI. If you’re wondering whether to learn SwiftUI or UIKit, I recommend reading this article: SwiftUI vs UIKit: Which Apple Framework Should You Learn?
When developing iOS apps with Storyboard in Xcode, UIKit is used for all aspects of the user interface. UIKit is the framework that forms the core components of all iOS apps. Among all the UIKit classes, views and view controllers are the most commonly used.
View controllers (UIViewControllers) play a crucial role in apps, acting as the skeleton of the app. A view controller is basically a class that manages a set of views (for display to the user) and coordinates with model objects (data).
The various widgets, such as Button, Label, TextField, and Switch, are represented by UIView subclasses.
Some UIKit classes are used to run the application (UIApplication), load images (UIImage), manage the device (UIDevice), the window (UIWindow), and some to define the delegates we use to configure the application and scenes (UIApplicationDelegate and UIWindowSceneDelegate). However, the framework also defines two basic classes for creating and managing views: UIView and UIViewController.
Subclasses of the UIView class are designed to present on-screen information, such as labels or images, or controls such as buttons, sliders, or switches. Subclasses of the UIViewController class are designed to manage the entire interface of a screen. They present views for displaying information and include the functionality needed to process their values and interact with the user.
Although Apple continues to support UIKit for app development, it’s clear that for Apple, the SwiftUI framework is the future. SwiftUI also makes it easier to develop apps for iOS, macOS, tvOS, iPadOS, and watchOS without making significant code changes.
If, on the other hand, you have existing projects that predate the introduction of SwiftUI, then it probably makes sense to leave the existing code unchanged, build future additions to the project using SwiftUI, and integrate these additions into your existing codebase.
For those unfamiliar with UIKit, a screen displayed in an app is typically implemented using a view controller (implemented as an instance of UIViewController or a subclass thereof).
Creating a struct that conforms to UIViewRepresentable
The individual components that make up the user interface of a UIKit-based application are derived from the UIView class. Buttons, labels, text views, maps, sliders (to name a few) are all subclasses of the UIKit class, UIView.
To facilitate integrating a UIView-based component into a SwiftUI view declaration, SwiftUI has the UIViewRepresentable protocol. To integrate a UIView component into SwiftUI, the component needs to be wrapped in a structure that implements this protocol.
The UIViewRepresentable protocol defines a structure that acts as a container for objects created from the UIView class and its subclasses. A structure that complies with this protocol can present a UIKit view within a SwiftUI interface.
At a minimum, the structure of the The container must implement the following methods to comply with the UIViewRepresentable protocol:
makeUIView() : This method is responsible for creating the component instance based on the UIView, performing any necessary implementation, and returning it.
updateView() : Called whenever a change occurs within the containing SwiftUI view that requires the UIView to update itself.
The following method can also be implemented:
dismantleUIView() : Provides an opportunity to perform cleanup operations before the view is disposed.
To include a UIView object, or an object created from any of its subclasses, in a SwiftUI interface, we must define a structure that complies with the UIViewRepresentable protocol and implement these methods. The makeUIView() and updateUIView() methods are mandatory. In the makeUIView() method, we must create the UIKit view instance and return it. We can use the updateUIView() method to update the view with values from the SwiftUI interface, such as values entered by the user in a text field. example. This method is also called when there are changes to the renderable view, so we can use it to update the view when the screen orientation changes.
If we want to send values from the UIKit view to the SwiftUI interface, we must implement the makeCoordinator() method. From this method, we must create an instance of a coordinator object and return it.
Example using UIKit in SwiftUI
Let’s look at an example to understand how to integrate UIKit into SwiftUI.
Let’s assume there’s a feature of the UILabel class that isn’t available in SwiftUI‘s text view. To encapsulate a UILabel view with a UIViewRepresentable so that it can be used in SwiftUI, the structure could be implemented as follows:
import SwiftUI
struct MyUILabel: UIViewRepresentable {
var text: String
func makeUIView(context: UIViewRepresentableContext<MyUILabel>) -> UILabel {
let myLabel = UILabel()
myLabel.text = text
return myLabel
}
func updateUIView(_ uiView: UILabel, context: UIViewRepresentableContext<MyUILabel>) {
}
}
With the UILabel view encapsulated, it can now be referenced within SwiftUI as if it were a built-in SwiftUI component:
struct ContentView: View {
var body: some View {
MyUILabel(text: "Hello UIKit from SwiftUI!")
}
}
Which in Xcode would give the following result:

Obviously, UILabel is a static component that doesn’t need to handle user interaction events. However, for views that need to respond to events, you need to extend the UIViewRepresentable container to implement a coordinator.
If you have any questions about this article, please contact me and I’ll be happy to help 🙂 You can contact me on my X profile or on my Instagram profile.