paint-brush
Asynchronous Programming in iOS: How to Speed Up Enterprise Apps Without Breaking Developers' Brainsby@hacker8965561

Asynchronous Programming in iOS: How to Speed Up Enterprise Apps Without Breaking Developers' Brains

by Vitalii ShapovalovDecember 4th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Asynchronous programming is a way to organize work in iOS apps. We'll explore approaches from the classic GCD to the modern magic of Swift Concurrency. Asynchronous programming helps speed up an app without adding chaos to the code.
featured image - Asynchronous Programming in iOS: How to Speed Up Enterprise Apps Without Breaking Developers' Brains
Vitalii Shapovalov HackerNoon profile picture

When all processes in an app work seamlessly, it's not magic, but well-configured asynchronous programming.


If your app doesn't respond instantly to user actions, a red flag pops up in the user's mind: "This is slow. This is inconvenient. Maybe I should delete it?" In enterprise applications, where every second counts, this is unacceptable.


In this article, we'll talk about how to organize asynchronous work in iOS apps. We'll explore approaches from the classic GCD to the modern magic of Swift Concurrency and show how they help speed up an app without adding chaos to the code.

Why Do We Need Asynchronous Programming?

Let's start with a simple analogy. Imagine you're standing in line for coffee. If the barista prepares each order from start to finish without starting the next one, the line will move slowly. But if one person brews the coffee, another froths the milk, and a third takes orders, the process moves faster.


The same thing happens in apps. To prevent the interface from "hanging" while the app loads data or processes files, these tasks are performed in the background. This is called asynchronous programming.

Three Approaches to Asynchronous Programming in iOS

Each of these approaches has its strengths and weaknesses. Let's examine them with examples.

GCD (Grand Central Dispatch): Fast, Simple, but With Pitfalls

GCD is like the old reliable Swiss army knife: it's a universal tool, but not always safe. With GCD, you can quickly launch tasks in the background and then return to the main thread to update the UI.


Example: Loading Data from the Server

Suppose your app displays a catalog of apps that need to be fetched from the server. With GCD, this is done like this:


func fetchAppData(completion: @escaping ([AppData]) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        let data = loadDataFromServer() // Load data
        DispatchQueue.main.async {
            completion(data) // Return result to update UI
        }
    }
}


Important Note

Everything related to the UI must always be on the main thread. If you forget this, you might experience crashes or strange UI behavior.


When to Use

Simple tasks like loading data or small computations.

OperationQueue: When Tasks Depend on Each Other

Imagine you're not just brewing coffee, but first buying beans, then grinding them, and then brewing. Here, you need to maintain a strict order.


OperationQueue helps you easily manage dependencies between tasks.


Example: Downloading and Processing an Image

Your app needs to download app icons and then process them before displaying. The solution:


let downloadOperation = DownloadImageOperation(url: imageURL)
let resizeOperation = ResizeImageOperation()

resizeOperation.addDependency(downloadOperation) // Processing starts after downloading

let queue = OperationQueue()
queue.addOperations([downloadOperation, resizeOperation], waitUntilFinished: false)


Why Is This Convenient?

You can set dependencies between tasks. For instance, you shouldn't process an image until it's downloaded.


When to Use

Complex tasks with dependencies that need to be performed in a specific order.

Swift Concurrency: A Modern Way to Write Asynchronous Code

If GCD and OperationQueue are old-school, then Swift Concurrency is the future. Instead of tangled closures, you write code that looks sequential but runs asynchronously.


Example: Sequential Server Requests

Suppose your app needs to perform three actions in sequence: check permissions, load a list of apps, and get details. This is easily written as follows:


func fetchUserPermissionsAndApps() async throws -> [AppData] {
    let permissions = try await fetchUserPermissions()
    guard permissions.contains(.accessToApps) else {
        throw NSError(domain: "AccessDenied", code: 403, userInfo: nil)
    }
    return try await fetchAppList()
}


Why Is This Great?

The code becomes clear and linear as if it were synchronous.


When to Use

All new projects. If you're starting from scratch, use Swift Concurrency.

Comparing Approaches

To illustrate the main differences:

Approach

Advantages

Disadvantages

Example Usage

GCD

Fast, built-in to iOS

Requires careful thread management

Simple background tasks, UI updates

OperationQueue

Supports task dependencies

Slightly more complex to use

Complex operation chains

Swift Concurrency

Readable, modern

Requires Swift 5.5 and above

Sequential requests, new projects

How to Choose an Approach?

  • You're new to a project that already uses GCD: Stick with GCD if there are only a few tasks.
  • You have a complex task system: OperationQueue is your choice.
  • You're writing a new app: Forget the old ways and use Swift Concurrency.

Conclusion

Asynchronous programming in iOS is not just a technique; it's a philosophy. When choosing an approach, remember:


  • GCD is fast and simple but risky for complex tasks.

  • OperationQueue is your helper if you need to manage dependencies.

  • Swift Concurrency is the modern standard for writing clean and safe code.


Tip: Experiment! Try all three approaches to understand which one works best for you.