From Old to New: How To Use Existing UIKit Views in SwiftUI
While introducing SwiftUI into an existing project, it may be beneficial to create a bridge between existing UIKit views and newer SwiftUI instead of rewriting the UIKit code in SwiftUI. In this example we will explore bridging a simple UITextField that may be part of a larger custom UIView for use in SwiftUI. The two key components are ensuring there is a way to observe changes in the text field as the user types and recognizing when the user submits the value.
Bridging UIKit and SwiftUI
First, create a struct that conforms to UIViewRepresentable. This is the struct that will ultimately be used in lieu of the TextField type. Inside this struct, add the necessary variables that were previously passed into the TextField. In this case, there is an @Binding text variable and an onCommit closure. Note that the following code will not compile as it stands, and it is shown in small steps to avoid confusion.
Add the requisite functions to connect the custom view and SwiftUI view. The makeUIView() function is where the UITextField is configured. This function is called by the system when the view is created for the first time. The updateUIView() function allows for updating the custom view upon a state change. SwiftUI calls this method when that state change occurs.
Create a Coordinator class that conforms to NSObject and UITextFieldDelegate. This does not have to be nested inside the custom view. It is a good practice because there may be multiple Coordinators for different UIKit views, and this method provides clean name-spacing. Two variables are needed here with the first being parent, which is the custom SwiftUI view containing the Coordinator class.
The second variable is onCommit, a closure that executes when the user submits the value in the text field. This is to provide similar functionality to the TextField's onCommit in SwiftUI and is passed from the custom view.
The necessary delegate functions must be implemented inside the Coordinator class as well. The textFieldDidChange() method allows for updating the text field when SwiftUI observes a state change. The textFieldDidEndEditing() is called when the text field resigns as first responder. This is where the onCommit closure is executed and the text from the text field is passed through.
Add the makeCoordinator() function to the custom view, initialize with self as the parent and pass the onCommit closure through.
By the end, the CustomTextView looks like this:
Using a custom view in SwiftUI
Now this custom view is ready to be used in SwiftUI. Replace the TextField code with the CustomTextView ensuring to pass the @State variable for the text field in. Additionally, the onCommit closure can be used with trailing closure syntax.
Wrapping up
This provides a similar API to the native SwiftUI TextField view. By learning how to wrap a UIKit view for SwiftUI, you can build a bridge for many types that do not natively support SwiftUI at this time. Additionally, if you run into any corner cases with SwiftUI, you can draw on UIKit knowledge to solve these problems.
SwiftUI has made some huge updates, and we are excited to see where it goes moving forward. Feel free to reach out directly with any questions, and make sure to follow our Application Development focus area for the latest topics and updates.