Fast SwiftUI apps without hacks

🚀 Build SwiftUI apps that stay fast

SwiftUI has delivered on a lot of promises. Less boilerplate. More declarative logic. Faster development. But when it comes to performance—especially in larger apps—things can get messy. Laggy scrolling, janky animations, views updating when they shouldn't...

This post dives into practical ways to keep your SwiftUI apps running smoothly. No hacks or obscure workarounds—just simple, solid techniques that’ll still make sense six months from now.

1. Less @State, more structure

@State is easy to use, but overusing it leads to unnecessary view updates. That hurts performance.

A better pattern: move state into a ViewModel using @StateObject or @ObservedObject. This separates your business logic from your UI and limits view updates to what’s actually changing.

Pro tip: Use Binding if child views need to write back to the same state.

2. Use EquatableView to reduce redraws

If a custom view is expensive to render and keeps updating even when the input hasn’t changed, you’re wasting cycles.

Fix: Wrap the view in EquatableView. It’ll compare the inputs and skip redraws if nothing changed.

EquatableView(content: MyHeavyView(data: myData))

Or make your model conform to Equatable so SwiftUI can optimize under the hood.

This gives you predictable performance in complex UIs.

3. Lazy views are your best friend

List, LazyVStack, and LazyHStack only load content when it’s visible. Essential for performance with long datasets.

Be careful: If you use ForEach inside a plain VStack, everything renders at once. That’s fine for 10 items—not for 200.

4. Measure before you guess

If you're serious about optimizing, start measuring.

Use os_signpost to mark up parts of your views or async work and analyze it in Instruments.

This gives you data, not just guesses, about where your UI is slowing down.

5. Use task and onChange with care

SwiftUI’s .task and .onChange(of:) modifiers are powerful, but can trigger more often than you think.

For example:

Adding the id: helps make sure the task only runs when it should. Otherwise, it could be re-triggered by unrelated changes in the view hierarchy.

📚 Want to go deeper?

🧠 Final thoughts

Performance in SwiftUI isn’t magic. With a few deliberate choices—less state, lazy loading, proper measurements—you’ll build apps that feel fast and stay fast.

SwiftUI is growing up. Are you ready to level up with it?

➡️ Subscribe to our newsletter for more practical dev tips.

Or check out our full blog archive for past insights.

Previous
Previous

Building for visionOS: Architecture That Lasts

Next
Next

Let AI Run Your Monday Morning Ops Meeting — So You Can Focus on Strategy