Keeping PDF Loading Off the Main Actor

The first hardening pass fixed a problem users would feel before they could hear a single word: waiting for a document to open.

The problem

PDF text extraction is local work, but a large document can still take enough time to make a SwiftUI screen feel stuck. A second problem shows up when a reader leaves while a load is still running: old text can arrive after the screen has moved on.

// Before: synchronous work tied to the view task
let text = try DocumentTextLoader.load(from: url, kind: document.kind)
self.text = text

The change

I moved DocumentTextLoader behind a small Swift actor. The reader now awaits the result and checks cancellation before publishing it to the view. That keeps extraction away from the main actor and stops a cancelled load from replacing the current document.

// After: isolate the loader and ignore cancelled work
actor DocumentTextLoader {
    static let shared = DocumentTextLoader()

    func load(from url: URL, kind: DocumentKind) throws -> String { ... }
}

let loadedText = try await DocumentTextLoader.shared.load(from: url, kind: kind)
try Task.checkCancellation()
text = loadedText
Open documentLoad in actorCheck cancellationRender text

Result

The UI has one clear loading path, and the reader only displays text that belongs to the active document. This is a small change, but it removes a class of timing bugs without adding another dependency.

← Back to all articles