← Back to list

How to Make Your Mobile App Faster

From instant performance gains to behind-the-scenes engineering tricks.

Essam Fahmy in Deloitte UK Engineering Blog · 2026-03-16 13:01 · 55 claps · 14.6 min read
#mobile-app-development #ios-app-development #android-app-development #mobile-app-performance #mobile-and-front-end
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

How to Make Your Mobile App Faster

From instant performance gains to behind-the-scenes engineering tricks.

How to Make Your Mobile App Faster. Image generated by ChatGPT (OpenAI).

How to Make Your Mobile App Faster. Image generated by ChatGPT (OpenAI).

We usually don’t think about app performance when everything works smoothly. But the moment an app takes a few seconds too long to load, frustration kicks in, and users leave.

And honestly, it’s not just users; even during development, performance is rarely at the top of people’s minds. In most teams, the mindset is “get it working first, optimise later if we need to”. Performance becomes an afterthought… until it’s suddenly the thing everyone panics about.

Some quick facts:According to a 2025 snapshot, the two major stores now list ≈ 2.12 million apps on Google Play Store and ≈ 2.05 million apps on Apple App Store (42matters, 2025). • 70% of users quit an app if it takes too long to load (OuterBox Design, 2024). • 40% move to competitors after a bad mobile experience (OuterBox Design, 2024). • 80% uninstall an app if it fails them three times or less (MoldStud, 2025). • Almost half of the apps are uninstalled within the first month (OuterBox Design, 2024).

When users leave a slow app. Homer Simpson backing into bushes, GIF from GIPHY.

When users leave a slow app. Homer Simpson backing into bushes, GIF from GIPHY.

The million-dollar question then: how do we make our mobile app faster?

There are two broad categories of ways to do this:

  1. Making speed improvements users can feel immediately: caching data, optimising images, and reducing app launch times. These are the optimisations that shape a user’s very first impression.
  2. Playing with the behind-the-scenes work, including writing efficient code, optimizing client-server interactions, and monitoring performance in real-world conditions. These practices ensure that your app remains reliable and scalable long after its launch.

In this article, we’ll look at a few practical techniques from both sides that can help make your mobile app faster and more reliable.

***Table of Contents:

  1. Cache Data Effectively
  2. Optimize Images
  3. Speed Up App Launch
  4. Keep Code Clean and Efficient
  5. Optimize Client-Server Interaction
  6. Monitor with Observability
  7. Resources***

Cache Data Effectively

Caching is one of the easiest ways to improve speed. This means storing data on the device so the app doesn’t need to fetch it from the server each time.

Types of caching:

  • In-memory caching (RAM)

This is when you store recently accessed data in memory using tools like NSCache (iOS) or LruCache (Android).

But not everything benefits equally; for example, cached text or small JSON objects won’t magically make the UI feel faster. Where it really shines is media-heavy content, especially images.

Think about how Instagram loads: when you scroll through your feed, the app pulls images from memory if you recently viewed them. This avoids refetching or redecoding large assets. The result? Smooth, uninterrupted scrolling even through image-heavy lists.

  • Disk caching (persistent storage)

Here, data is stored on the device in a more permanent way using SQLite, Core Data, Room, or a similar framework. This reduces repeated network calls and gives your app offline access.

A simple real-world example: A news app may locally store the latest fetched articles. Even if the user opens the app without an internet connection, they can still browse the most recently synced content. When the connection returns, the app refreshes only the changed data instead of downloading everything again. This greatly improves perceived speed and reduces server load.

Cache Invalidation Strategies

Keeping cached data fresh is as important as caching data itself; we don’t want users to see old data when they’re expecting new data or updates in the app. Thus, we use conditional caching:

  • Time-based: Refresh data after a set duration.
  • Event-based: Clear cache when specific events occur, maybe it is an event related to your project’s business, or the server could indicate to us that the data has changed.
  • Version-based: Invalidate cache when the app or API version changes.

A clean caching layer typically sits between the network and repository layers to ensure the UI always gets the fastest available data, while background tasks update it silently when needed:

Network → Cache → Repository → ViewModel → UI

Security Considerations

While caching improves performance, it can also introduce risks if the cached data includes anything sensitive. Not all cached information needs protection (e.g., public images or non-personal metadata), but some types do, especially anything tied to a specific user (i.e., personally identifiable information (PII)) or account. In these cases, security must also be integrated into our caching strategy.

  • Use secure storage (Keychain and EncryptedSharedPreferences)

Best for: authentication tokens, refresh tokens, session identifiers, or anything long-lived and sensitive. These storages offer OS-level encryption and access control, ensuring sensitive credentials aren’t exposed even if the device is compromised.

  • Encrypt disk caches when they hold personal data

Best for: offline user profiles, chat history, private documents, or cached API responses containing personal data. Files stored on disk are more easily inspected, so encryption protects the data even if someone gains filesystem access.

  • Clear cached data when the user logs out or switches accounts

Best for: any app supporting multiple accounts or shared-device usage to prevent cross-account data exposure. User B should never see User A’s cached content, even unintentionally.

This ensures your caching system stays both fast and safe.

Pro tips:

  • Cache changes that occur rarely (e.g., app configuration, user preferences). These values are stable and don’t require frequent network calls. Caching them avoids unnecessary API hits. Examples include feature flags, theme settings, language choice, and remote config values.
  • Don’t over-cache; large caches waste storage and slow down access time. Caching everything increases disk usage and can degrade performance as reading large cache files takes longer. Also, excessive caching may lead to cache thrashing, where frequently evicted items are repeatedly re-fetched. This creates extra network requests, higher CPU usage, and slower app performance.

Rule of thumb: Cache only the data that meaningfully improves user experience. For example, media, feed items, or app configurations rather than every API response.

  • Not all API responses should be cached. It depends on the business needs and how it impacts user experience. For example, news articles or product listings are good to cache, but real-time prices and live match scores are not supposed to be cached.

Optimise Images

Images are the biggest contributors to slow apps. They take time to load, consume bandwidth, and use memory.

Best practices:

  • Resize Before Upload

Uploading full resolution (e.g., 4032×3024) camera images is unnecessary most of the time. What we can often do is resize them to the dimensions in which they will be presented to the user. For example, scaling down full-resolution images to 1080px x 1080px.

*This can be done by: * ** — iOS: UIImage → .jpegData(compressionQuality:) — Android: Use Bitmap scaling or libraries like Coil / Glide.

  • Compress Images

Compression reduces image size with minimal visual loss. JPEG compression at a quality level of (0.7–0.8) typically achieves optimal file size reduction with minimal perceptual loss *(Google Developers, 2024).*

You can also use modern compression libraries if needed (e.g., TinyPNG API, ImageMagick) before uploading.

  • Use Modern Image Formats (HEIC/WebP/AVIF).

*This can be done by: * ** — iOS: Capture in HEIC (default) → convert to JPEG only if needed — Web/Android: Prefer WebP or AVIF for product images.

  • Lazy Load Images

Lazy-loading is a concept whereby resources are only fetched at the exact moment they’re needed. In the context of images, the fetching of these images can be delayed until they enter the view, reducing unnecessary network and memory usage.

This improves scrolling performance and speeds up initial screen load, especially in image-heavy feeds.

Potential Downside: While lazy loading improves performance, it introduces a few considerations, as users may briefly see placeholders or blank areas if images load too slowly. Also, the critical images should still be preloaded for immediate visibility, so the best practice is to lazy-load non-critical content.

*For iOS: Use lazy containers combined with asynchronous image loading so assets load only when cells appear:

  • LazyVStack / LazyHStack in SwiftUI
  • List with AsyncImage or custom loaders with caching*

*For Android: Use recycling containers with smart image loaders:

  • RecyclerView for efficient view reuse
  • Libraries like Coil, Glide, or Picasso, which load images only when views are visible, cancel in-flight requests when items scroll off-screen, and integrate memory + disk caching automatically*

Remember: Don’t fetch a 2000px photo to display a 200px thumbnail. If we are building an e-commerce app, product thumbnails should be pre-compressed and sized correctly.

Use a CDN to Deliver Images Faster

A Content Delivery Network (CDN) is a globally distributed network of servers that stores cached copies of your images. Instead of every user downloading images from your main server (which might be far away), a CDN serves them from a location close to the user. This reduces latency, increases download speeds, and lowers your backend load.

CDNs not only theoretically improve performance, but they also deliver measurable speed gains in real-world usage. In practice, serving images through a CDN combined with automatic resizing and modern formats such as WebP or AVIF can reduce image load times by 40–70% while significantly lowering payload sizes *(Cloudflare, 2024).*

By serving content from geographically closer servers and dynamically generating right-sized images, CDNs minimize latency, bandwidth consumption, and decoding overhead on mobile devices, resulting in smoother scrolling and a more responsive user experience, particularly on slower networks (*Cloudflare, 2024; Google Developers, 2024*).

Why CDNs Make Mobile Apps Faster

  • Lower latency: Images load from the nearest geographic server.
  • Higher reliability: CDNs are built to handle heavy traffic and spikes.
  • Automatic optimization: Many CDNs can resize, compress, and convert images to efficient formats like WebP/AVIF, offloading work from your API and the backend team can focus on logic, not file serving.

All of this leads to faster image loading, smoother scrolling, and a more responsive UI, especially on slower networks. We take advantage of these CDN benefits by requesting optimised versions of each image using URL parameters. Instead of downloading the full-resolution file, the CDN generates a resized, compressed version on the fly based on parameters like width, quality, or format. For example, the app might request:

https://cdn.example.com/product/123?w=200&q=70&format=webp

This ensures the device only downloads the exact size it needs for the UI, reducing bandwidth, memory usage, and decoding time without extra logic inside the app.

Pro tips:

  • Test on older devices, low memory + big images = crashes.
  • Apply caching for images used repeatedly.

Speed Up App Launch

First impressions matter. Nearly half of users expect an app to launch in two seconds or less.

Understanding App Startup Types:

The speed at which an app launches greatly affects first impressions, user retention, and engagement. To optimize launch performance effectively, it helps to understand the three main startup types:

  • Cold start: App starts fresh (most resource-heavy)

In a cold app launch, your app is starting from scratch. This means that the app’s process has not been created by the system until then; this is a time-consuming process for the phone’s operating system, as memory must be allocated, old resources evicted to make space for that memory, etc.

  • Warm start: App was closed but still in memory

In a warm start, the app was backgrounded long enough for the system to kill it, but not long enough to be totally evicted, or the user had left the app recently. So, the OS was able to reclaim parts of the application that still exist in memory, most typically the previously used code pages, cached resources, compiled bytecode, and some system-level memory mappings. The OS can reuse these preloaded components, allowing the app to avoid repeating expensive initialization work.

  • Hot start: App is already running in the background

A hot start occurs when the app process is still active in the background. The system restores it immediately: the activity/view controller tree, in-memory objects, UI state, and app logic are all already loaded. The app just needs to resume from its suspended state, making it nearly instantaneous.

Ways to reduce launch time:

  • Initialise only critical services (auth, configs) at startup (e.g., auth, configs): Reduces the workload during cold starts, so the app reaches the first screen faster.
  • Defer analytics, ads, or logs until after launch: Minimizes background work during cold and warm starts, allowing the UI to load smoothly.
  • Simplify the first screen layout (less nesting, lighter UI): Speeds up rendering across all startup types, especially noticeable in cold starts.
  • Batch network requests (fetch headlines + user settings in one call): Reduces total network latency, benefiting cold and warm starts on multiple requests.
  • Use placeholders for images until real data is ready: Improves perceived load time during cold and warm starts.
  • Optimize assets: pre-load small icons and critical images. Ensures that even hot starts feel snappy and responsive.

Example: A news app that takes several seconds to launch can significantly improve its startup time by deferring non-critical initialization and caching frequently accessed data, such as top headlines.

Pro tips:

  • On iOS, apps that take longer than 20 seconds to launch get killed by the system.
  • On Android, Google suggests keeping cold start under 5 seconds.
  • Splash screens don’t reduce load time but make the wait feel smoother.

Keep Code Clean and Efficient

Poorly written code = slow, buggy apps. Regular code hygiene is essential.

Best practices:

  • Update libraries: Use the latest SDKs for security and performance.

Keep in touch with your outdated dependencies as they can slow down your app or introduce bugs, and regularly remove unused dependencies. Every library increases app size and startup time.

  • Fix memory leaks: Make sure objects get deallocated when not in use.

A memory leak happens when your app keeps objects in memory even after they are no longer needed, and the active scope of your application has no reference to those objects. These objects remain in memory, wasting resources and potentially slowing or crashing the app.

  • Run heavy tasks off the main thread

Network requests, JSON parsing, or file operations should not block the UI. For example, A news app that parses JSON on the main thread may freeze during updates. Moving parsing to a background thread keeps the UI responsive.

  • Don’t run constant background services unless necessary.
  • Cache and display last-viewed content instead of showing errors.

Example 1: Memory Leak in Swift

In iOS (Swift), memory leaks often occur due to strong reference cycles in closures or class references. In Android (Java/Kotlin), memory leaks can happen when you keep references to activities, contexts, or views longer than necessary.

class ViewController: UIViewController {
    // ViewController holds a reference to the Timer.
    var timer: Timer?
    override func viewDidLoad() {
        super.viewDidLoad()
        // Strong reference cycle: timer holds self strongly
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            self.updateUI()
        }
    }
    func updateUI() {
        print("Updating UI")
    }
}

This creates a strong reference cycle: Because of this cycle, the ViewController never gets deallocated, even when you leave the screen. That’s a memory leak.

ViewController → Timer → Closure → ViewController

Fixed version using [weak self]:

override func viewDidLoad() {
    super.viewDidLoad()

    // Weak reference to the view controller. Fixed it!
    timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
        self?.updateUI()
    }
}

What changed?

**[weak self] tells the closure: **“Don’t hold a strong reference to self. If self goes away, it becomes nil instead of keeping it alive.” Now, when the ViewController is dismissed, it can be deallocated, and the memory is freed.

Example 2: Freezes UI in Swift

let data = try! Data(contentsOf: url) // Blocks main thread
let json = try! JSONSerialization.jsonObject(with: data)

Efficient version using a background thread:

// Start the parsing work on a background thread
DispatchQueue.global(qos: .background).async {
    if let data = try? Data(contentsOf: url),
       let json = try? JSONSerialization.jsonObject(with: data) {
        // Switch back to the main thread to update the UI
        DispatchQueue.main.async {
            // Update UI with parsed data
        }
    }
}

Pro tips:

  • Use Instruments (iOS) or Android Profiler to identify bottlenecks.
  • Optimize slow methods and memory leaks, then re-profile to confirm.
  • Test on all types of devices, especially low-end ones.
  • Refactor regularly: Old code piles up technical debt.

Optimise Client-Server Interaction

Apps often fail not because of bad code, but because of poor code concurrency.

Scenario:

  • The mobile app makes 5 network requests to load the main screen.
  • Average network latency per request = 500 ms (0.5 seconds).
  • Each request has a payload of 200 KB.
  • Users consider the app “slow” if the screen takes >2 seconds to load.

If requests are sequential (one after another):

Total network time = 5 network requests × 0.5s / network request = 2.5s

Even without considering rendering time, the network alone exceeds the 2-second threshold, which is enough to make users perceive the app as slow.

Optimisation opportunity

If requests are parallelised (all 5 at once), the total network time ≈ 0.5 s, well below the threshold. If responses are cached, repeated visits can load instantly (0s network delay).

Best practices:

  • *Focus on fetching only what you need.
  • Instead of requesting entire datasets, fetch just the data needed for the current screen. For example, a travel app should fetch only flight names and prices in the search results. Full details, reviews, and high-resolution images can load later when the user taps a flight.
  • *Server-side filtering and pagination.
  • Never fetch all results and filter locally. Use query parameters on the server to let the server perform the filtering:
GET /flights?from=NYC&to=LAX&fields=flightName&page=1&limit=20

Fields: Returns only flight names.

Pagination: Loads 20 results at a time.

This reduces payload size, speeds up loading, and improves the experience for users on poor networks.

  • Use CDNs: Distribute images, videos, and static content closer to the user.
  • Avoid redirects: Every redirect adds extra latency.
  • Test on poor networks: Use Network Link Conditioner (iOS) or Android Profiler.

Pro tips:

  • Combine multiple requests into one batch when possible.
  • Use concurrent API request when possible.
  • Use caching headers so repeat requests hit the cache instead of the network.
  • Monitor API response times to spot backend bottlenecks.
  • Large payloads not only slow the app but can drain the battery.

Monitor with Observability

Performance work doesn’t end at launch; the real test starts when users interact with your app in unpredictable, real-world conditions.

This is where observability becomes essential. It helps teams detect performance issues early — often before they turn into bad reviews and gives teams the feedback loop they need to continuously improve performance long after release.

Key Metrics to Monitor

  • Crashes: Use Crashlytics or Sentry to capture errors with stack traces.
  • Performance metrics: Track launch times, slow screens, and memory usage.
  • Network latency: Measure API response times, especially during peak hours.
  • Resource-heavy screens: Identify slow-loading UI (image-heavy, animations).
  • Battery and memory usage: Watch out for leaks or background processes.
  • Device & OS distribution: Some issues only appear on older devices.
  • Cold vs warm launches: Measure them separately; they behave differently.
  • Offline/poor network: Monitor how your app reacts to unstable connectivity.

Crashlytics: The “How” Behind Finding Real Root Causes

Crashlytics isn’t just a crash counter.

Realizing there’s more going on than expected. Scene from Twin Peaks. GIF from GIPHY.

Realizing there’s more going on than expected. Scene from Twin Peaks. GIF from GIPHY.

The real power comes from signal + context:

  • Breadcrumbs

Automatically logs navigation steps, background/foreground events, and custom logs you add.

Crashlytics.log("Tapped checkout button")
  • Non-fatal errors

Most performance problems never cause a full crash.

You can log request timeouts, decoding errors, failed login attempts, and memory warnings. Over time, you’ll see patterns, e.g., “80% of timeout errors come from the restaurant list endpoint.”

  • Performance signals

Crashlytics integrates with Firebase Performance, giving you: ***- slow screen render warnings

  • ANR-like freezes (Application Not Responding)
  • cold/warm launch durations***
  • ***User impact heatmap

    You instantly know whether an issue affects 0.1% or 40% of sessions. This helps prioritize what to fix first, instead of guessing.

Example: A food delivery app notices spikes in non-fatal errors for the restaurant list. Crashlytics shows that all affected users are on older devices, and the UI thread is blocked for a few seconds during JSON parsing. The team moves parsing off the main thread → the spike disappears.

A spike is a sudden, unusual increase in a metric that normally stays stable, for example, crashes, network errors, timeouts, or slow-screen reports. You usually see it as a sharp jump on your monitoring dashboard.

Pro tips:

  • Set up alerts for spikes in crash rates or non-fatal errors.
  • Watch session length: Drops may indicate performance issues.
  • Use logs + metrics to connect errors to real user behaviour.

Conclusion

Performance is more than just a technical metric; it’s the backbone of user experience. Fast apps feel smooth, reliable, and trustworthy, while slow ones frustrate users and drive them away. By combining user-facing optimizations like caching, image management, and launch improvements with solid engineering practices such as clean code, efficient networking, and observability, you ensure your app not only meets user expectations today but scales gracefully for tomorrow.

Remember: Performance isn’t a one-time task, it’s a mindset. Measure, optimize, and monitor continuously. Every millisecond counts, and the improvements you make can directly translate into happier users, longer engagement, and a stronger app reputation. Start small, iterate smartly, and watch your app’s speed, and user satisfactio.

Thanks for reading! I hope these techniques help you build faster, smoother apps. Good luck building!

Resources

Here are some resources for the above info, for anyone interested.

Note: This article speaks only to my personal views / experiences and is not published on behalf of Deloitte LLP and associated firms, and does not constitute professional or legal advice.

All product names, logos, and brands are property of their respective owners. All company, product and service names used in this website are for identification purposes only. Use of these names, logos, and brands does not imply endorsement.


메타데이터
post_id
2fd1be7f503a
slug
how-to-make-your-mobile-app-faster-2fd1be7f503a
url
https://medium.com/deloitte-uk-cloud-blog/how-to-make-your-mobile-app-faster-2fd1be7f503a
canonical_url
https://medium.com/deloitte-uk-cloud-blog/how-to-make-your-mobile-app-faster-2fd1be7f503a
author_url
https://medium.com/@essammohamedomran
status
ok
fetched_at
2026-06-15 20:49:13