Every new iOS project starts with the same fork in the road: SwiftUI vs UIKit. Most articles answer with “SwiftUI is the future” and stop there. That is true, but it is not a decision. A decision needs constraints: your minimum deployment target, your animation requirements, the SDKs you are forced to integrate, and the people who will maintain the code in three years.
This guide is written for the person who has to sign off on the architecture, not for someone choosing a tutorial. Here is the short version, then the reasoning behind it.
The short answer (2026 edition)
- Default to SwiftUI for any new app that can target iOS 17 or later. It is the fastest path to a shipping product and it is where Apple invests every single year.
- Keep UIKit in your toolbox, not as the foundation but as an escape hatch. You will need it, and that is fine.
- Go UIKit-first only if you fall into one of a small number of categories: very low deployment targets, heavy custom drawing and timeline editing, deeply interruptible custom transitions, or an existing codebase with hundreds of screens.
- The realistic answer for most teams is “both”, with SwiftUI as the default layer and UIKit wrapped behind representable types.

SwiftUI vs UIKit at a glance
| Criterion | SwiftUI | UIKit |
|---|---|---|
| Paradigm | Declarative, state driven | Imperative, event driven |
| Speed to first screen | Very fast | Slower, more boilerplate |
| Layout model | Parent proposes, child chooses | Auto Layout constraints and frames |
| Fine grained control | Good, with occasional walls | Total |
| Backward compatibility | Tied to OS version, API gaps below iOS 17 | Stable back to very old targets |
| Third party SDK support | Improving, many still UIKit under the hood | Universal |
| Hiring pool | Large and growing, skewed junior to mid | Smaller, skewed senior |
| Apple platform reach | iOS, iPadOS, macOS, watchOS, tvOS, visionOS, widgets | iOS, iPadOS, tvOS (AppKit on macOS) |
| Debuggability of layout | Harder, opaque view tree | Mature tooling, view hierarchy debugger |
1. Layout systems: the real difference nobody explains
People describe SwiftUI as “a prettier way to write UIKit code”. That is misleading. The layout engines work on opposite principles, and this is where teams get hurt when they migrate mentally without migrating conceptually.
UIKit: you place things
Auto Layout is a constraint solver. You describe relationships, the engine resolves a single valid solution, and you can inspect the result at any time. If two constraints conflict, the console tells you exactly which ones. You can always ask a view “what is your frame right now” and get an answer. For the wider picture, see Use SwiftUI with AppKit and UIKit.
The cost is verbosity, constraint spaghetti in long-lived screens, and the eternal risk of a broken layout that only appears on one device size.
SwiftUI: the parent proposes, the child decides
There is no constraint solver. A parent proposes a size, the child returns the size it actually wants, and the parent positions it. That is the entire model. It makes 90% of layouts trivial and makes the remaining 10% feel like magic tricks you have to learn one by one: fixedSize, layoutPriority, GeometryReader, custom Layout conformances, and alignment guides.
Practical takeaway: if your designs are made of stacks, lists, forms, cards and adaptive grids, SwiftUI will cut your layout code by half or more. If your designs are made of overlapping, pixel anchored, measurement dependent compositions (audio waveforms, timelines, seating charts, canvas editors), UIKit or a custom Layout will save you pain. The topic gets a thorough treatment elsewhere.

2. Animation and gesture control
This is the section where UIKit veterans dig in, and they have a point, although a smaller one than in 2022.
What SwiftUI does better
- State driven animation. Change a value inside
withAnimationand everything that depends on it animates. No manual teardown. - Transitions and matched geometry.
matchedGeometryEffectand the newer zoom navigation transitions give you hero animations in a handful of lines. - Spring physics by default, with interruptible, velocity preserving behaviour that used to require serious UIKit work.
- Phase and keyframe animators for multi stage sequences without nested completion blocks.
What UIKit still does better
- Direct manipulation at high frequency. In UIKit you can mutate a layer transform on every gesture callback with zero indirection. SwiftUI routes everything through state and a re-evaluation of
body, which is fast in recent releases but is still an extra layer between finger and pixel. - Fully custom, interruptible navigation transitions with percent driven interaction. SwiftUI has improved a lot here but UIKit still gives you the complete control surface.
- Core Animation access. Anything involving
CAEmitterLayer, customCALayersubclasses, layer masks driven per frame, orCADisplayLinksynchronised drawing is simply more natural in UIKit.
Note that this is not an all or nothing choice. A single screen can be SwiftUI while a gesture heavy canvas inside it is a UIViewRepresentable.
3. Backward compatibility: the constraint that decides for you
SwiftUI is bundled with the OS, so your minimum deployment target defines which SwiftUI you actually get. This is the single most decisive factor in the SwiftUI vs UIKit debate, and it is a business question more than a technical one.
| Minimum target | What you get | Recommendation |
|---|---|---|
| iOS 18 and later | Modern navigation, Observation, mature scrolling APIs, refined transitions, strong concurrency story | SwiftUI first, no hesitation |
| iOS 17 | @Observable, scroll position APIs, solid NavigationStack |
SwiftUI first, expect a few availability checks |
| iOS 16 | Workable, but you lose Observation and several scroll and layout refinements | SwiftUI is viable, budget for polyfills |
| iOS 15 or lower | Missing modern navigation, plenty of behavioural bugs, heavy if #available branching |
UIKit first, or raise the target |
With UIKit, an API added in iOS 13 behaves the same on iOS 18 as it did on day one. With SwiftUI, the same code can render differently across OS versions because the framework itself changed. That is the hidden tax of declarative UI, and you pay it in QA time.
Rule of thumb: check your own analytics before you argue about this. If under 3% of your users sit below iOS 17, raising the deployment target is usually cheaper than maintaining two UI code paths for a year.

4. Hiring and team skills
Framework choice is a staffing decision. Two realities coexist in 2026:
- New iOS developers learn SwiftUI first. Bootcamps, Apple’s own tutorials and most courses are SwiftUI-led. If you build a UIKit-only codebase, every new hire has a ramp up period before touching production code.
- Senior iOS developers still know UIKit deeply, and that knowledge is exactly what unblocks you when SwiftUI hits a wall. The people who can debug a broken hosting controller sizing issue are the ones who understand the layers underneath.
What to put in your job description
- Require SwiftUI as the primary skill for product feature work.
- Require UIKit interop literacy:
UIViewRepresentable,UIHostingController, view controller lifecycle. Not full UIKit mastery, just enough to bridge. - For at least one senior on the team, require real UIKit and Core Animation experience. That is your insurance policy.
A pure SwiftUI team ships faster until the day it does not, and then it stalls for a week on something a UIKit veteran would fix in an afternoon.
5. Third party library and SDK support
This gets better every year but it is still uneven. Categorise your dependencies before committing:
| SDK category | SwiftUI readiness |
|---|---|
| Analytics, crash reporting, feature flags | No UI, works identically. No concern. |
| Networking, persistence, DI | UI agnostic. No concern. |
| Payments and checkout | Official SwiftUI wrappers are common now, sometimes presenting a UIKit sheet internally. |
| Maps | Native MapKit for SwiftUI is production ready. Third party map SDKs usually need a wrapper. |
| Chat, video calls, live streaming | Mostly UIKit view controllers with thin SwiftUI wrappers. Expect interop. |
| Advertising and mediation | Still UIKit centric. Banner and native ad views need representables. |
| PDF, rich text, document editing | UIKit and TextKit based. Wrapping is mandatory. |
| Camera, barcode, AR overlays | AVFoundation preview layers are UIKit. Wrap once, reuse everywhere. |
The pattern is clear: anything that renders its own UI is likely UIKit inside. That is not a blocker, it is a half day of wrapper code, but it means “pure SwiftUI” is a marketing phrase rather than an achievable state for a commercial app.
6. Performance: is SwiftUI fast enough now?
For the vast majority of apps, yes. The honest nuance:
- Static and moderately dynamic screens: indistinguishable from UIKit.
- Long lists and grids: fine if you use lazy containers correctly, stable identifiers, and avoid recomputing expensive values in
body. A poorly writtenListwill stutter where a well writtenUICollectionViewwould not. - Continuous, gesture driven, per frame updates: UIKit still has the edge because it lets you bypass the data flow layer and touch the layer directly.
- App launch: SwiftUI’s runtime cost at startup has come down significantly, but a very large SwiftUI view tree built eagerly is still a launch time risk.
The biggest SwiftUI performance problems in real projects are almost never the framework. They are unnecessary view invalidation caused by badly scoped observable state. Migrating to the Observation framework and profiling with the SwiftUI instrument solves most of them.

7. Which app types still justify UIKit in 2026
Be specific rather than ideological. Choose UIKit as the primary framework if you recognise your product here:
Strong case for UIKit first
- Media and timeline editors. Video trimmers, audio DAWs, multi track scrubbing, frame accurate playheads. Constant per frame layout plus Core Animation control.
- Drawing and canvas apps. Infinite canvases, vector editors, CAD-like tools, anything with custom hit testing and zoomable coordinate spaces.
- Rich text and document editors. TextKit 2 work, custom input views, complex selection behaviour.
- Apps that must support iOS 15 or older. Emerging market reach, enterprise MDM fleets, long lived hardware companions.
- Large existing UIKit codebases. If you already have 200 screens, a rewrite is a business risk, not a technical upgrade. Incremental adoption is the correct move.
- Highly bespoke navigation. Products where the navigation itself is the brand: custom interruptible transitions, card stacks with physics, multi layer overlay routing.
Weak case for UIKit (people think they need it but do not)
- “We have a complex form”: SwiftUI is excellent at forms.
- “We need a collection view with custom layout”: SwiftUI grids plus a custom
Layoutcover most designs. - “We need a camera”: wrap the preview layer once, done.
- “We care about performance”: measure first, this is rarely the bottleneck.
- “Our designers are demanding”: SwiftUI animation is a strength, not a weakness.
8. The mixed approach: SwiftUI inside an existing UIKit app
This is how most professional teams actually operate, and Apple has doubled down on the incremental adoption story, including a dedicated WWDC26 session on using SwiftUI with UIKit and AppKit. Here is the practical playbook.
Step 1: pick a low risk screen
Start with something self contained: settings, onboarding, an empty state, a detail screen. Avoid the screen with the custom transition and the third party ad banner.
Step 2: host a SwiftUI view in a view controller
import SwiftUI
import UIKit
struct SettingsView: View {
@State private var notificationsEnabled = true
var body: some View {
Form {
Toggle("Notifications", isOn: $notificationsEnabled)
LabeledContent("Version", value: "4.2.0")
}
}
}
final class SettingsViewController: UIHostingController<SettingsView> {
init() {
super.init(rootView: SettingsView())
title = "Settings"
}
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You can now push it from any existing UIKit navigation controller as if it were a normal view controller.
Step 3: embed SwiftUI as a child, not a full screen
final class DashboardViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let chart = UIHostingController(rootView: RevenueChartView())
addChild(chart)
chart.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(chart.view)
NSLayoutConstraint.activate([
chart.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
chart.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
chart.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
chart.view.heightAnchor.constraint(equalToConstant: 220)
])
chart.didMove(toParent: self)
}
}
Step 4: use SwiftUI for cells inside existing collection views
UIHostingConfiguration lets you keep your battle tested UICollectionView while writing cell content declaratively. This is often the highest value, lowest risk migration in a mature app.
let registration = UICollectionView.CellRegistration<UICollectionViewCell, Product> { cell, _, product in
cell.contentConfiguration = UIHostingConfiguration {
ProductRowView(product: product)
}
.margins(.horizontal, 16)
}
Step 5: go the other direction when you need UIKit power
struct CameraPreview: UIViewControllerRepresentable {
let session: CaptureSession
func makeUIViewController(context: Context) -> CameraViewController {
CameraViewController(session: session)
}
func updateUIViewController(_ controller: CameraViewController, context: Context) {
controller.update(with: session)
}
}
Step 6: share state cleanly
Use a single @Observable model type that both worlds read. SwiftUI observes it automatically. UIKit can observe it with withObservationTracking or with a small Combine or async stream bridge. The important rule is that the source of truth lives outside both UI layers, so neither framework owns your business logic.
Interop pitfalls to plan for
- Sizing. A hosting controller does not automatically shrink to its content unless you configure its sizing options. Set explicit constraints or use
sizingOptions. - Safe areas. Nested hosting controllers can apply safe area insets twice. Disable them on the child when the parent already handles it.
- Navigation ownership. Decide early whether UIKit or SwiftUI owns routing. Mixing
NavigationStackinside a pushed hosting controller creates double navigation bars and broken back gestures. - Keyboard avoidance. SwiftUI handles it automatically, UIKit does not. Nested combinations can fight each other.
- Lifecycle.
onAppearis notviewDidAppear. Do not assume a one to one mapping for analytics events.

9. A decision checklist you can actually use
- What is the minimum iOS version the business requires? Below iOS 16, lean UIKit. iOS 17 and above, lean SwiftUI.
- Does the core experience involve per frame custom rendering or timeline scrubbing? If yes, UIKit for that surface.
- Do you need to ship on watchOS, visionOS or widgets too? SwiftUI is the only reasonable answer.
- How many UI heavy third party SDKs are mandatory? More than three, budget interop time regardless of your choice.
- Who maintains this in two years? Optimise for the framework your future hires will already know.
- Is this a new app or an evolution of an existing one? New means SwiftUI. Existing means incremental adoption, never a rewrite.
If you answer those six questions honestly, the SwiftUI vs UIKit debate resolves itself in about ten minutes.
Our recommendation at coding4
For a greenfield iOS project starting today, we build SwiftUI first with a UIKit safety net:
- Deployment target at iOS 17 or 18 unless data proves otherwise.
- SwiftUI for all screens, navigation and state.
- A dedicated interop module where every
UIViewRepresentablelives, so UIKit dependencies are isolated and testable. - Business logic in plain Swift types with
@Observable, completely independent of the UI framework. That way, if a screen has to be rewritten in UIKit next year, only the view layer changes.
That last point matters more than the framework choice itself. The teams that suffer are not the ones that picked the wrong framework, they are the ones that put business logic inside their views. The same thing is done cleanly on academyofcreativeeducation.com.
FAQ
Will SwiftUI replace UIKit?
Not in the sense of UIKit disappearing. UIKit is the foundation that large parts of the system and thousands of SDKs still rely on, and Apple keeps updating it. What is already happening is that SwiftUI has replaced UIKit as the default for new development, while UIKit becomes the low level layer you reach for when you need it.
What are the downsides of using SwiftUI?
The main ones: behaviour that varies across OS versions, a view tree that is harder to inspect when layout goes wrong, fewer escape hatches for very fine grained control, occasional performance cliffs caused by over broad state invalidation, and a dependency on your minimum deployment target for access to newer APIs.
Can I use UIKit with SwiftUI in the same app?
Yes, and most production apps do. Use UIHostingController and UIHostingConfiguration to put SwiftUI into UIKit, and UIViewRepresentable or UIViewControllerRepresentable to put UIKit into SwiftUI. Mixing both is a supported, documented, Apple recommended strategy rather than a hack.
Is the SwiftUI List lazy?
List loads its rows lazily, similar to a table view, so it only creates the rows it needs. However, if you build the list from a ForEach over a fully materialised array, the data itself is all in memory even if the views are not. For very large or paginated data sets, combine lazy containers with paging and stable identifiers.
Should a beginner learn SwiftUI or UIKit first?
SwiftUI first. It gets you to a working app faster and it is where the job market is heading. Learn UIKit second, focused on interop, view controller lifecycle and Auto Layout basics, because you will meet UIKit in any real codebase.
Is SwiftUI slower than UIKit?
For typical app UI, no meaningful difference today. UIKit retains an advantage for continuous, gesture driven, per frame updates where bypassing the data flow layer matters. In practice, the performance issues teams encounter with SwiftUI are usually caused by state design, not by the framework.
Which one is better for iPad and Mac apps?
SwiftUI, by a wide margin, because a single codebase adapts across iPhone, iPad, Mac, Vision, Watch and widgets. If you need deep Mac specific behaviour, you will still bridge into AppKit for certain controls.
Need help deciding, or migrating an existing UIKit app screen by screen? Our iOS team at coding4 does exactly this kind of audit and incremental adoption work. Get in touch and we will review your deployment targets, dependencies and animation requirements before a single line of code is written.

