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.
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.
Each of these approaches has its strengths and weaknesses. Let's examine them with examples.
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.
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.
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.
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 |
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.