Protocol¶
UIViewRepresentable¶
A view to import a UIKit view into SwiftUI.¶
Declaration¶
protocol UIViewRepresentable : View where Self.Body == Never
Overview¶
Use this protocol to port a UIView from UIKit into SwiftUI.
Setup¶
To conform to UIViewRepresentable, you must implement four main lifecycle functions:
- makeUIView(context:): Create and return an instance of your UIViewType here.
- updateUIView(_:context:): This is immediately called once after the call to makeUIView(context:), then called whenever any state changes.
- dismantleUIView(_:coordinator:): Upon destruction of the parent container, this gets called. (This is optional. A default implementation is provided.)
- makeCoordinator(): This creates a Coordinator for the view. (This is also optional.)
Port a simple UIView¶
To port a simple UIKit view, UIActivityIndicatorView, you could use the following setup:
struct ActivityIndicator: UIViewRepresentable {
typealias Context = UIViewRepresentableContext<Self>
typealias UIViewType = UIActivityIndicatorView
let isAnimated: Bool
public func makeUIView(context: Context) -> UIViewType {
UIActivityIndicatorView(style: .medium) // create the instance of the view
}
public func updateUIView(\_ uiView: UIViewType, context: Context) {
// Check if isAnimated is true, and if the view is inactive.
if isAnimated && !uiView.isAnimating {
uiView.startAnimating() // Animate
}
// Check if isAnimated is false, and if the view is active.
if !isAnimated && uiView.isAnimating {
uiView.stopAnimating() // Stop animating
}
}
}
- In updateUIView(_:context:), you must handle isAnimated being either true or false.
- No redundant calls are made to startAnimating and stopAnimating. Redundant calls are guarded against by checking whether uiView.isAnimating is true or not. This is a general principle for optimizing the performance of your UIViewRepresentable.
Having implemented it as a UViewRepresentable, you could now use it in SwiftUI. For example:
struct ContentView: View {
@State var isAnimating: Bool = false
var body: some View {
VStack {
Toggle("Animating", isOn: $isAnimating)
ActivityIndicator(isAnimated: isAnimating)
}
}
}

In this example, the ActivityIndicator from before is used and can be toggled by passing a boolean to ActivityIndicator.init(isAnimated:).
Context-aware UIViewRepresentables¶
SwiftUI heavily relies on the environment, by way of environment objects (environmentObject(_:)) and environment values (EnvironmentValues). Environment values are useful for creating intelligent and context-aware UIKit ports.
For example, we can remove the isAnimated parameter from ActivityIndicator, and use isEnabled from the environment instead via EnvironmentValues's isEnabled:
struct ActivityIndicator: UIViewRepresentable {
typealias Context = UIViewRepresentableContext<Self>
typealias UIViewType = UIActivityIndicatorView
public func makeUIView(context: Context) -> UIViewType {
UIActivityIndicatorView(style: .medium)
}
public func updateUIView(\_ uiView: UIViewType, context: Context) {
if context.environment.isEnabled && !uiView.isAnimating {
uiView.startAnimating()
}
if !context.environment.isEnabled && uiView.isAnimating {
uiView.stopAnimating()
}
}
}
The disabled(_:) modifier is responsible for modifying isEnabled. The example usage must be updated to use disabled(_:) instead of isAnimated:
struct ContentView: View {
@State var isAnimating: Bool = false
var body: some View {
VStack {
Toggle("Animating", isOn: $isAnimating)
ActivityIndicator()
.disabled(!isAnimating)
}
}
}

Using disabled(_:), the activity indicator can now be made active or inactive.
The benefits of using the environment and context become apparent when multiple views are used together. For example:
struct ContentView: View {
@State var isAnimating: Bool = false
var body: some View {
VStack {
Toggle("Animating", isOn: $isAnimating)
VStack {
ActivityIndicator()
ActivityIndicator()
ActivityIndicator()
}
.disabled(!isAnimating)
}
}
}

A single View/disabled(_:) modifier on the VStack can now become responsible for enabling/disabling the activity indicators inside the stack. This also allows parent views, that aren't aware of ContentView's implementation, to control whether views within it are enabled or disabled.
Implementing UIKit delegates for a UIViewRepresentable¶
UIKit views often require delegates to communicate events and pass data back and forth with their view controllers. Since SwiftUI is all about modularity and componentization, there is no concept of a view controller. UIViewRepresentable uses the concept of a coordinator to port views reliant on the delegate pattern.
Learn more about delegation in Swift here.
For example, here is an example port of UISearchBar:
struct SearchBar: UIViewRepresentable {
let placeholder: String
@Binding var text: String
func makeCoordinator() -> Coordinator {
// Create an instance of Coordinator
Coordinator(self)
}
func makeUIView(context: Context) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.placeholder = placeholder
searchBar.delegate = context.coordinator
return searchBar
}
func updateUIView(\_ uiView: UISearchBar, context: Context) {
// Set the search bar's text to the value of the binding
uiView.text = text
}
class Coordinator: NSObject, UISearchBarDelegate {
var parent: SearchBar
init(\_ searchBar: SearchBar) {
parent = searchBar
}
func searchBar(\_ searchBar: UISearchBar, textDidChange searchText: String) {
// Set the binding's value to the latest search bar text
parent.text = searchText
}
}
}
The Coordinator is the glue between a SwiftUI-style binding-pattern and a UIKit-style delegate-pattern. A Coordinator is a state object created and maintained for the lifetime of a UIViewRepresentable, that typically implements the primary delegate for the UIKit view being ported. In this case, the UIViewType is UISearchBar and its primary delegate type is UISearchBarDelegate.
Text input controls in SwiftUI often take a String Binding in their initializer, and use it to get/set the latest text entered by the user via control. It's extremely important to note that bindings are bidirectional, because SwiftUI ports of UIKit views must also get the latest input from the Binding. UIKit patterns typically only require notifying the observer and thus setting the values, but in SwiftUI it is imperative to handle both to be a good SwiftUI citizen.
Here is example of using the search part port:
struct ContentView: View {
@State var searchText: String = ""
var body: some View {
VStack {
SearchBar(placeholder: "Enter something here", text: $searchText)
Text("Entered text: \(searchText)")
}
}
}

In this example, SearchBar is bound to a state variable, searchText. To confirm that values are being written as they are input, a Text reflects the latest value of the state variable.
- Note: If searchText's initial value is used to set the initial text of the UISearchBar represented by SearchBar. This means if the initial value of searchText was set to "Bananas 🍌🍌", SearchBar' would have an initial search text of "Bananas 🍌🍌" already loaded.
Further notes¶
- Creating UIKit views are expensive. That is why they are done once per lifetime of a UIViewRepresentable.
- Because the same UIViewType instance is reused as much as possible, updateUIView(_:context:) is responsible for making sure that the parameters passed via the initializer, and the SwiftUI environment, are always in sync with the UIKit view being managed by the representable.
Availability¶
iOS 13.0+
tvOS 13.0+
Topics¶
Type Alias¶
Context A type alias for the representable's context.
Type Method¶
dismantleUIView(_:coordinator:) Cleans up the presented UIKit view (and coordinator) in anticipation of their removal.
dismantleUIView(_:coordinator:) Cleans up the presented UIKit view (and coordinator) in anticipation of their removal.
Instance Method¶
makeCoordinator() Creates the custom instance that you use to communicate changes from your view to other parts of your SwiftUI interface.
makeCoordinator() Creates the custom instance that you use to communicate changes from your view to other parts of your SwiftUI interface.
makeUIView(context:) Creates the view object and configures its initial state.
updateUIView(_:context:) Updates the state of the specified view with new information from SwiftUI.
Instance Property¶
body Declares the content and behavior of this view.
Associated Type¶
Coordinator A type to coordinate with the view.
UIViewType The type of view to present.