Grand Central Dispatch for concurrency

Sofia Martinez Jan 2026
1 tab
import Foundation
import UIKit

class ImageProcessor {
    // Background processing with main thread updates
    func processImage(_ image: UIImage, completion: @escaping (UIImage?) -> Void) {
        DispatchQueue.global(qos: .userInitiated).async {
            // Heavy processing on background thread
            let processedImage = self.applyFilters(to: image)

            // Return to main thread for UI updates
            DispatchQueue.main.async {
                completion(processedImage)
            }
        }
    }

    private func applyFilters(to image: UIImage) -> UIImage? {
        // Simulate heavy processing
        Thread.sleep(forTimeInterval: 2)
        return image
    }
}

// Dispatch groups for coordinating multiple tasks
class DataSynchronizer {
    func syncAllData(completion: @escaping (Bool) -> Void) {
        let group = DispatchGroup()
        var hasError = false

        group.enter()
        fetchPosts { success in
            if !success { hasError = true }
            group.leave()
        }

        group.enter()
        fetchComments { success in
            if !success { hasError = true }
            group.leave()
        }

        group.enter()
        fetchUsers { success in
            if !success { hasError = true }
            group.leave()
        }

        group.notify(queue: .main) {
            completion(!hasError)
        }
    }

    private func fetchPosts(completion: @escaping (Bool) -> Void) {
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 1)
            completion(true)
        }
    }

    private func fetchComments(completion: @escaping (Bool) -> Void) {
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 1)
            completion(true)
        }
    }

    private func fetchUsers(completion: @escaping (Bool) -> Void) {
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 1)
            completion(true)
        }
    }
}

// Custom serial queue for thread-safe access
class ThreadSafeCache<Key: Hashable, Value> {
    private var cache: [Key: Value] = [:]
    private let queue = DispatchQueue(label: "com.myapp.cache", attributes: .concurrent)

    func get(_ key: Key) -> Value? {
        queue.sync {
            cache[key]
        }
    }

    func set(_ value: Value, forKey key: Key) {
        queue.async(flags: .barrier) {
            self.cache[key] = value
        }
    }

    func remove(_ key: Key) {
        queue.async(flags: .barrier) {
            self.cache.removeValue(forKey: key)
        }
    }
}

// Debouncing with dispatch work items
class SearchDebouncer {
    private var workItem: DispatchWorkItem?
    private let delay: TimeInterval

    init(delay: TimeInterval = 0.3) {
        self.delay = delay
    }

    func debounce(action: @escaping () -> Void) {
        workItem?.cancel()

        let newWorkItem = DispatchWorkItem(block: action)
        workItem = newWorkItem

        DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: newWorkItem)
    }
}
1 file · swift Explain with highlit

Grand Central Dispatch (GCD) manages concurrent code execution with dispatch queues. The main queue handles UI updates—I use DispatchQueue.main.async to return to main thread after background work. Global queues provide concurrent background execution with different quality of service levels: .userInteractive, .userInitiated, .utility, and .background. Custom queues offer fine-grained control over concurrency. Dispatch groups coordinate multiple async tasks, waiting for all to complete with group.notify(). Semaphores limit concurrent access to resources. For modern Swift, async/await is preferred, but GCD remains essential for legacy code and certain use cases like debouncing and rate limiting.