2019: Early adoption
SwiftUI released in 2019, to tremendous excitement across the community. Unfortunately, it really sucked for that first year.
I was building a satanic side hustle at the time, and figured I’d go all-in. I blindly cobbled together a global @EnvironmentObject to run the prototype. Push navigation was mostly broken, so I was forced to make every screen transition a modal. I fell in love with the flawed framework.
By the time I did a real startup the next year, I was confident enough to make a proper bet on SwiftUI. This second version, on iOS 14, was much more stable. SwiftUI let me ship faster than I dreamed possible with UIKit. By my third startup, I targeted iOS 15+ and churned out screens like a maniac.
Like all great writers, my story has a 6-year time skip.
2025: The summer everything changed
Two twin technological happenstances collided this summer, transforming the way I work day-to-day:
Firstly, UIKit got some love from Apple. All of a sudden, the iOS 17 @Observable macro was ported to UIKit, alongside updateProperties(), a new view delegate method. It was even back-deployed to work on iOS 18.
Perhaps the eternal MVVM debate between Combine, RxSwift, closures, and binding libraries can finally be put to rest. Or, maybe it will just make the argument worse.
Secondly, agentic AI tools took off in a big way. Improvement in context windows, foundation models, and the tool calling innovation triggered an inflection point where CLI tools started working really well. Pasting code into ChatGPT now feels a bit ‘90s.
Why do people use iOS?
There’s a reason Apple is easy-breezy* when it comes to open-sourcing Swift, xnu, libdispatch, and Foundation. There are myriad security, community goodwill, and cross-platform benefits from keeping these low-level frameworks in the open.
*I’m sure there are several Apple engineers (that aged 20 years during open-sourcing negotiations with legal) who may disagree with the term “easy-breezy”.
When it comes to SwiftUI, UIKit, Core Animation, and most system libraries, however, Apple is ruthlessly guarded.
The difference is clear: Apple is in the hardware business. Integrated system UX is a core competitive advantage, and so Apple does not want you to enjoy their UI frameworks on other people’s platforms.

The secret sauce of iOS has, from day 1, been its characteristic look and feel, what Apple calls the human interface: Interaction. Responsiveness. Animation.
When you drill down into the limits of these 3 pillars, you will find the gap between UIKit and SwiftUI is starkest.
Is SwiftUI ready for production?
For years, developers have asked is SwiftUI production-ready?
For years, the answer has been yes, but.
SwiftUI gets better every year, but is eternally on the long road to parity with UIKit. Even today, basic stuff like camera previews need to come wrapped in a UIHostingController.
SwiftUI also suffers worse performance out-of-the-box due to overhead from diffing view states and dynamic layout computation. This is being worked on, but is fundamentally an architectural choice that may never quite be as fast as UIKit. Every magic system has a cost.
Everybody forgave SwiftUI’s shortcomings because of the undeniable boost to development speed and the tidy ease-of-use the API offers.
But what happens when you’re no longer writing every line of code manually?
Is SwiftUI dead?
This year, UIKit quietly became production-ready again, with Apple granting access to modern @Observable macros and introducing updateProperties().
Simultaneously, agentic AI tooling has trivialised the overhead of writing imperative layout boilerplate. Two key disadvantages of UIKit, slow development and verbose APIs, have been negated. The models are also trained on mountains of UIKit code and docs. Good luck one-shooting fresh SwiftUI APIs like TextEditor.
In 2025, the question is unavoidable:
Should you start migrating back to UIKit?
We need to understand whether SwiftUI can ever fully match UIKit in API capability and performance. Let’s look at the code that reveals the definitive answer.
I won’t tell you how to live your life, but if you want the optimal UX for your users, maximum animation performance, and total control over interactions, UIKit will remain the undisputed king.
Delegates don’t actually suck
I almost cried last year when I touched UIScrollViewDelegate for the first time since 2019.
I was building a cute interaction that animated a line of emojis along a circular path, with their motion along the circle linked to your scroll offset. I could trivially access scrollView.contentOffset.y from the scrollViewDidScroll(_:) delegate method.
Do you know how hard this is with SwiftUI preference keys!?
To be completely fair to SwiftUI, onScrollGeometryChange (iOS 18) can do this now. At the time, I was supporting iOS 17 and couldn’t touch it.
But there are hundreds of UIKit delegate methods that remain unsupported, like scrollViewWillEndDragging(_:withVelocity:targetContentOffset:).
Even if SwiftUI achieves parity with the full UIScrollViewDelegate, we are subjected to a combinatorial explosion of memorisation for new views, view modifiers, and their respective positions on the view hierarchy.
Don’t even get me started on namespacing.
It almost feels like… maybe delegates were actually quite a good pattern for implementing the iceberg of nuance underneath the world’s most complex UI system.
That’s actually a pretty great metaphor…
The UIKit Iceberg
This is the case for pretty much every SwiftUI API that exists. Porting everything from a nearly-20-year-old framework isn’t happening anytime soon.
My controversial opinion about architecture
UIKit lends itself inevitably to “complex” architectures, such as MVC, MVVM, and VIPER. Often, you’ll split your view controller into files for every delegate applied, and every link in the chain of data-flow.
SwiftUI lends itself to simpler architectures, such as the very hot MV approach.
But here’s my edgy opinion:
The more complex your screen-level architecture is, the harder it is to write code. But the easier it is to read. If you keep strict standardisation, you’ll always know where the gesture interactions will live, and what route your data takes as it propagates from tap, to model, to animation.
The simpler your architecture is, the easier it is to write code. With a good design library, you can smash out a SwiftUI screen in an hour. The more you can reduce services into property wrappers, the faster you can ship.
But God help the next engineer who touches it.
The default standardisation from MV is, essentially, “put the stuff wherever”. It just doesn’t lend itself to consistency. Gesture handling will sit on the view that feels the most convenient at the time. If you’re lucky, interaction code sit vertically near to your state, otherwise you’ll just have to search the file(s) to hunt down what touches the data and when.
VIPER is a pain in the arse to write, but frankly, I always knew where to find what I needed.






