Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

iOS 18 new RecognizedTextRequest DEADLOCKS if more than 2 are run in parallel

Following WWDC24 video "Discover Swift enhancements in the Vision framework" recommendations (cfr video at 10'41"), I used the following code to perform multiple new iOS 18 `RecognizedTextRequest' in parallel. Problem: if more than 2 request are run in parallel, the request will hang, leaving the app in a state where no more requests can be started. -> deadlock

I tried other ways to run the requests, but no matter the method employed, or what device I use: no more than 2 requests can ever be run in parallel.

func triggerDeadlock() {}
    try await withThrowingTaskGroup(of: Void.self) { group in
        // See: WWDC 2024 Discover Siwft enhancements in the Vision framework at 10:41                
        
		// ############## THIS IS KEY
        let maxOCRTasks = 5 // On a real-device, if more than 2 RecognizeTextRequest are launched in parallel using tasks, the request hangs
		// ############## THIS IS KEY
		        
        for idx in 0..<maxOCRTasks {
			
            let url = ... // URL to some image
			
            group.addTask {                
                // Perform OCR
                let _ = await performOCRRequest(on: url: url)
            }
        }
        var nextIndex = maxOCRTasks
        for try await _ in group { // Wait for the result of the next child task that finished
            if nextIndex < pageCount {
                group.addTask {
					let url = ... // URL to some image
                    
	                // Perform OCR
	                let _ = await performOCRRequest(on: url: url)
                }
                nextIndex += 1
            }
        }
    }
}

// MARK: - ASYNC/AWAIT version with iOS 18
@available(iOS 18, *)
func performOCRRequest(on url: URL) async throws -> [RecognizedText] {

    // Create request
    var request = RecognizeTextRequest() // Single request: no need for ImageRequestHandler

    // Configure request
    request.recognitionLevel = .accurate
    request.automaticallyDetectsLanguage = true      
    request.usesLanguageCorrection = true
    request.minimumTextHeightFraction = 0.016
	
    // Perform request
    let textObservations: [RecognizedTextObservation] = try await request.perform(on: url)

    // Convert [RecognizedTextObservation] to [RecognizedText]
    return textObservations.compactMap { observation in
        observation.topCandidates(1).first
    }
}

I also found this Swift forums post mentioning something very similar.

I also opened a feedback: FB17240843

Hello,

Thank for the bug report and forum post citation.

We suspect that this is an issue with the number of MTLDrawable objects available to the Vision framework, and your bug report will help determine that.

Ideally, drawables are released as soon as possible but RecognizeTextRequest() may retain them beyond a frame boundary (time limit) causing the app to wait until another drawable becomes available (from the pool of drawables). If the app continues to request more drawables it will continue to wait. This can be verified in an Instruments trace.

That said, this appears to be a case of resource starvation rather than deadlock.

To work around this you can:

  1. Lower the cost of RecognizeTextRequest() e.g. try VNRequestTextRecognitionLevel.fast
  2. User lower resolution images
  3. Use all of the RecognizedTextObservation results from a single observation rather than aggregating the results of parallel requests

Re: 3, this raises a question: why are you attempting to perform multiple requests in parallel when a single request already provides multiple results? Is each request working on a different image containing text?

Yes, I perform OCR on multiple images in parallel. This is a use case of the app. Typically:

  • a background process might OCR images that haven't been OCR'd yet
  • the user can add new images where OCR has to be performed
  • etc...

Moreover, as suggested in the WWDC video, I wanted to use the new Swift Concurrency API to perform multiple requests in parallel. But as mentioned in the topic, their provided code won't work with RecognizeTextRequest.

My current workaround is unfortunately to serialize every OCR request, to avoid the issue. But this doesn't fit the spirit of the app and the architecture.

iOS 18 new RecognizedTextRequest DEADLOCKS if more than 2 are run in parallel
 
 
Q