UIKit to SwiftUI: The Migration Guide Every iOS Dev Needs
Moving from UIKit to SwiftUI? The transition isn’t always straightforward—here’s how to do it right.
Hello world,
As SwiftUI gains momentum, more developers are faced with the challenge of migrating their UIKit-based apps to this modern, declarative framework.
UIKit has been the foundation of iOS development for over a decade, but SwiftUI introduces a more intuitive and efficient approach to building user interfaces. The catch? The transition isn’t always straightforward—especially for complex apps.
This guide will help you map common UIKit components to their SwiftUI equivalents, answer key questions, and provide practical code examples to make the shift as smooth as possible.
Why Move to SwiftUI?
Before diving into the technical details, let’s tackle the big question: Why make the switch
Declarative Syntax – Define what the UI should look like, rather than building it step by step.
Live Previews – Xcode Previews let you see changes in real-time, cutting down development time.
Cross-Platform Support – Write once, run on iOS, macOS, watchOS, and tvOS.
Modern Features Built-In – Seamless support for dark mode, dynamic type, and localization.
That said, SwiftUI is still evolving, and some advanced use cases may require UIKit interoperability. Understanding how to bridge the gap between the two frameworks is key to a successful transition.
Let’s break it down.
1. Basic Components
UILabel → Text
UIKit:
let label = UILabel()
label.text = "Hello, UIKit!"
label.textColor = .black
SwiftUI:
Text("Hello, SwiftUI!")
.foregroundColor(.black)
UIButton → Button
UIKit:
let button = UIButton(type: .system)
button.setTitle("Tap Me", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
SwiftUI:
Button("Tap Me") {
print("Button tapped!")
}
UITextField → TextField
UIKit:
let textField = UITextField()
textField.placeholder = "Enter text"
SwiftUI:
TextField("Enter text", text: $inputText)
2. Layout and Containers
UIStackView → HStack/VStack
UIKit:
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.addArrangedSubview(label)
stackView.addArrangedSubview(button)
SwiftUI:
HStack {
Text("Hello")
Button("Tap Me") { /* Action */ }
}
UITableView → List
UIKit:
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
SwiftUI:
List(items, id: \\\\.self) { item in
Text(item)
}
UICollectionView → LazyVGrid/LazyHGrid
UIKit:
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
SwiftUI:
let columns = [GridItem(.adaptive(minimum: 100))]
LazyVGrid(columns: columns) {
ForEach(items, id: \\\\.self) { item in
Text(item)
}
}
3. Navigation
UINavigationController → NavigationStack
UIKit:
let navController = UINavigationController(rootViewController: viewController)
SwiftUI:
NavigationStack {
NavigationLink("Go to Detail", destination: DetailView())
}
UITabBarController → TabView
UIKit:
let tabBarController = UITabBarController()
tabBarController.viewControllers = [viewController1, viewController2]
SwiftUI:
TabView {
Text("Home").tabItem { Label("Home", systemImage: "house") }
Text("Settings").tabItem { Label("Settings", systemImage: "gear") }
}
4. Modals and Alerts
UIAlertController → Alert
UIKit:
let alert = UIAlertController(title: "Alert", message: "Something happened!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
SwiftUI:
.alert("Alert", isPresented: $showAlert) {
Button("OK", role: .cancel) { }
} message: {
Text("Something happened!")
}
Commonly Asked Questions
1. Can I use SwiftUI and UIKit together?
Yes! SwiftUI provides UIViewRepresentable
and UIViewControllerRepresentable
to embed UIKit components in SwiftUI, and UIHostingController
to embed SwiftUI views in UIKit.
Example:
struct MyUIKitView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .red
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
2. How do I handle complex animations in SwiftUI?
SwiftUI has built-in support for animations using modifiers like .animation()
and withAnimation
. For more complex animations, you can use UIViewRepresentable
to leverage UIKit’s animation capabilities.
3. Is SwiftUI ready for production?
SwiftUI is production-ready for most apps, but it still lacks some advanced features found in UIKit. For complex applications, a hybrid approach is often the best strategy during the transition. Experiment, adapt, and learn from experience!
4. How do I manage state in SwiftUI?
SwiftUI uses a declarative approach to state management.
Key property wrappers include:
@State
for view-local state.@Binding
for shared state between parent and child views.@ObservedObject
for external state (e.g., ViewModels).@EnvironmentObject
for global state.
Example:
class ViewModel: ObservableObject {
@Published var count = 0
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
VStack {
Text("Count: \\\\(viewModel.count)")
Button("Increment") {
viewModel.count += 1
}
}
}
}
That was fun.
Now, onto the next thing.
Thiago R.
System Thinker @ Systematic Success
Maker @ Making of a Maker
Engineering Manager, Apps @ Pluto TV / Paramount Global
Founder @ Ghost Ship & Co.
Digital Nomad @ Threads, X.com, Instagram, LinkedIn, GitHub, Website
PS... I launched Making of a Maker to document my adventure of building things on the internet. Think of it as my public hacker journal. If you’re a hacker too, join me for the ride!