DEV Community

ArshTechPro
ArshTechPro

Posted on

Understanding @preconcurrency in Swift

What is @preconcurrency?

@preconcurrency is a Swift attribute that was introduced in Swift 5.5 alongside the major concurrency overhaul that brought us async/await, actors, and structured concurrency. It was added specifically to solve the chicken-and-egg problem of adopting modern concurrency: how do you use Swift's new concurrent features when most of your existing code and dependencies weren't designed with these strict safety rules in mind?

The attribute serves as a compatibility bridge, allowing developers to gradually migrate to Swift's concurrency model without having to rewrite everything at once or deal with an overwhelming number of compiler warnings.

The Background: Why @preconcurrency Exists

When Swift 5.5 introduced structured concurrency, it came with strict rules about what types can safely cross concurrency boundaries (called Sendable types). This created immediate friction – suddenly, using UIKit in async functions, working with Core Data, or integrating popular third-party libraries would generate numerous warnings about potential thread safety issues.

Rather than forcing developers to choose between adopting modern concurrency or using existing frameworks, Apple introduced @preconcurrency as a pragmatic solution. It essentially tells the compiler: "This code predates Swift concurrency, so don't apply the strictest safety checks, but let me use it responsibly in concurrent contexts."

The Problem @preconcurrency Solves

Swift's concurrency system is strict about safety. It wants to prevent data races and ensure your concurrent code is rock-solid. But this creates a problem: lots of existing frameworks and libraries were written before these rules existed.

Without @preconcurrency, you'd see warnings like this:

import UIKit

class ViewController: UIViewController {
    func updateUI() async {
        // ⚠️ Warning: UIViewController is not Sendable
        self.view.backgroundColor = .blue
    }
}
Enter fullscreen mode Exit fullscreen mode

The warning appears because UIKit wasn't designed with Swift's concurrency rules in mind. But we know UIKit is safe to use on the main thread.

How @preconcurrency Fixes This

Add @preconcurrency to your import, and the warnings disappear:

@preconcurrency import UIKit

class ViewController: UIViewController {
    func updateUI() async {
        //  No warnings - Swift trusts you to use UIKit safely
        await MainActor.run {
            self.view.backgroundColor = .blue
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 1: Working with UIKit

Here's a practical example of loading and displaying an image asynchronously:

@preconcurrency import UIKit

@MainActor
class ImageViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        Task {
            await loadAndDisplayImage()
        }
    }

    func loadAndDisplayImage() async {
        do {
            // Load image on background queue
            let image = try await downloadImage(from: imageURL)

            // Update UI on main thread (thanks to @MainActor)
            imageView.image = image
        } catch {
            imageView.image = UIImage(systemName: "exclamationmark.triangle")
        }
    }

    private func downloadImage(from url: URL) async throws -> UIImage {
        let (data, _) = try await URLSession.shared.data(from: url)
        guard let image = UIImage(data: data) else {
            throw ImageError.invalidData
        }
        return image
    }
}
Enter fullscreen mode Exit fullscreen mode

Without @preconcurrency import UIKit, you'd get warnings about using UIKit types in async contexts.

Important Things to Remember

It's Your Responsibility: @preconcurrency doesn't make code thread-safe – it just stops the compiler from warning you. You still need to ensure your code is actually safe to use concurrently.

It's Temporary: Think of @preconcurrency as a migration tool. As frameworks get updated to support Swift concurrency properly, you should remove these annotations.

When NOT to Use @preconcurrency

  • New code:
  • When modern alternatives exist:
  • Uncertainty about safety

The Bottom Line

@preconcurrency is like a diplomatic passport for legacy code – it lets older APIs work smoothly in your modern async/await world. Use it thoughtfully during your transition to Swift concurrency, but always plan for the day when you won't need it anymore.

Top comments (1)

Collapse
 
arshtechpro profile image
ArshTechPro

The attribute serves as a compatibility bridge, allowing developers to gradually migrate to Swift's concurrency model without having to rewrite everything at once or deal with an overwhelming number of compiler warnings.