Skip to content
All writing

Performance

Why iOS Apps Feel Slow Even When The API Is Fast

A practical look at the client-side bottlenecks that make mobile apps feel slow after the backend has already done its job.

4 min read

Illustration for Why iOS Apps Feel Slow Even When The API Is Fast

The API is only one timer

Mobile teams often start a performance conversation with backend latency. The endpoint is 180 ms, the payload is cached at the edge, the database query is fine. Then somebody opens the app on an older phone and the screen still feels heavy.

That is not a contradiction. On iOS, the user does not experience API latency in isolation. They experience launch work, navigation state, JSON decoding, image loading, view invalidation, layout, diffing, persistence, main-thread contention and the timing of all of those things together.

A fast backend can still feed a slow app. The client can take good data and turn it into a bad experience.

The main thread tells the truth

If a screen stutters, freezes during navigation or ignores taps for a beat, start by asking what is happening on the main thread. It is common to find work there that nobody considers expensive because each piece looks harmless alone.

Date formatting in a cell. JSON mapping that grew over time. Image decoding after download. A large diff. A synchronous database fetch. A SwiftUI body invalidating more of the tree than expected. A logging call that does disk work. None of these needs to be dramatic to hurt the feel of the app.

The fix is not to move everything to background queues randomly. The fix is to identify the user-visible lane and protect it. Rendering, input and navigation need room. Expensive transformation, decoding, persistence and precomputation should happen where they cannot block touch and animation.

Images are usually a system, not an asset

A feed can have excellent API latency and still feel slow because images are treated as an afterthought. Large images arrive, get decoded at the wrong time, fight for memory, get requested again, or pop into cells after the user has already scrolled past them.

A serious image pipeline has several decisions: request sizing, disk cache, memory cache, downsampling, cancellation, placeholder behavior, prefetch distance and eviction policy. The right answer depends on the product. A shopping app, banking app, chat app and media feed do not all need the same image behavior.

The mistake is thinking of images as UI decoration. In many mobile products, images are the performance profile. They decide memory pressure, scroll smoothness, perceived loading and how quickly the screen becomes useful.

State can make a fast screen feel unstable

Performance is not only frame rate. A screen feels slow when it keeps changing shape. Content flashes in, disappears, reorders, shows stale values, then corrects itself. The user reads that as slowness even if every individual operation is technically fast.

This usually comes from unclear state ownership. The view loads cached data, then network data, then derived data, then an optimistic update, then a server correction. If those transitions are not designed, the screen feels nervous.

A better approach is to define the states explicitly: cached, refreshing, empty, failed, partial, pending action, synced. The app should not merely have data or not have data. It should know what kind of truth it is showing.

Measure the path the user feels

Do not stop at endpoint timing. Measure time to first meaningful content, scroll hitching, image cache hit rate, main-thread stalls, memory warnings, launch time, pagination delay, failed retries and how often the user sees a full-screen loading state.

The useful question is not whether the system is fast somewhere. The useful question is where the user waits, where the user loses trust and where the app blocks the next action.

Once you measure that path, performance work becomes less mystical. You can decide whether to precompute, cache, cancel, paginate earlier, reduce payload size, split state, defer work, or change the product behavior.

Where this fits

This essay belongs to the Design Systems path: Offline-first sync, caching, modularization, persistence, performance, platform strategy and architecture governance.

Keep going through the path.

Every article should lead to a next action, not a dead end.