Swift 6 AVAssetImageGenerator generateCGImagesAsynchronously

I have the following piece of code that works in Swift 5

func test() {
    let url = Bundle.main.url(forResource: "movie", withExtension: "mov")
    let videoAsset = AVURLAsset(url: url!)
    let t1 = CMTime(value: 1, timescale: 1)
    let t2 = CMTime(value: 4, timescale: 1)
    let t3 = CMTime(value: 8, timescale: 1)
    let timesArray = [
                NSValue(time: t1),
                NSValue(time: t2),
                NSValue(time: t3)
            ]
    let generator = AVAssetImageGenerator(asset: videoAsset)
    generator.requestedTimeToleranceBefore = .zero
    generator.requestedTimeToleranceAfter = .zero        
    generator.generateCGImagesAsynchronously(forTimes: timesArray ) { requestedTime, image, actualTime, result, error in
                
         let img = UIImage(cgImage: image!)
    }
     
}

When I compile and run it in Swift 6 it gives a

EXC_BREAKPOINT (code=1, subcode=0x1021c7478)

I understand that Swift 6 adopts strict concurrency. My question is if I start porting my code, what is the recommended way to change the above code?

Rgds, James

Answered by DTS Engineer in 799076022

Thanks for the crash report. It confirms that my force unwrap guess from yesterday is wrong. Check out the crashing thread backtrace:

Thread 16 Crashed::  Dispatch queue: com.apple.coremedia.assetimagegenerator.notifications
0 libdispatch.dylib          … _dispatch_assert_queue_fail + 116
1 libdispatch.dylib          … dispatch_assert_queue + 188
2 libswift_Concurrency.dylib … swift_task_isCurrentExecutorImpl(swift::SerialExecutorRef) + 280
3 Migrate.debug.dylib        … closure #1 in ContentView.test() + 256
4 Migrate.debug.dylib        … thunk for @escaping @callee_guaranteed (@unowned CMTime, @guaranteed CGImageRef?…
5 AVFCore                    … -[AVAssetImageGenerator _didGenerateCGImage:] + 540
6 AVFCore                    … aig_didGenerateCGImage + 44
7 CoreFoundation             … __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 120

Frame 7 shows that this code is running in response to a notification being posted. Notifications like this run synchronously. This is on thread 16, so the poster posted the notification on thread 16. Obvious that’s not the main thread (-:

Frames 6 and 5 are the AVFoundation code routing the callback to you. Frameworks 4 and 3 is code in your app, but probably not you code. Rather, it’s glue generated by the compiler. That’s calling down into the Swift runtime, frames 2 through 0, to check the current executor context. And that check has failed, resulting in the trap you’re seeing.

In short, it’s crashing before it runs any code in your completion handler.

What should you do about this? This isn’t really my area of expertise, but I’ll note that the docs for the method you’re using say:

Swift clients should prefer the asynchronous images(for:) method instead.

So, I’m thinking that the best path forward here is for you to follow that advice and adopt the images(for:) method. I believe that’ll avoid this problem and yield nicer code!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Are you sure that this is a Swift concurrency issue? In most cases those show up at compile time rather than at runtime, and the EXC_BREAKPOINT is a classic symptom of a Swift trap.

Which line triggers this failure?

Also, please post a crash report. Posting a Crash Report explains how to do that, including for the case where you’ve stopped in the debugger.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi

I am compiling it in Xcode 16 Beta (Swift 6) with iOS 18, and iPhone 15 Pro Max Simulator. The code compiles and run but crashes. If the following is commented out it is ok.

generator.generateCGImagesAsynchronously(forTimes: timesArray ) { requestedTime, image, actualTime, result, error in
            
     let img = UIImage(cgImage: image!)
}

I was hoping you’d post a crash report. Without that, I’m basically just guessing here.

The most likely cause of this trap is that image is nil and thus the image! is trapping. Looking at the docs for that callback, it includes an error value. Generally, if error is not nil then I'd expect image to be nil. But your code ignores error completely.

When you crash, what’s in error?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

Thanks for looking into this. After I detach from Xcode->Debug. I get the following in the attached report.txt file. The debugger did not yet reach the breakpoint in the generateCGImagesAsynchronously closure

Thanks

Accepted Answer

Thanks for the crash report. It confirms that my force unwrap guess from yesterday is wrong. Check out the crashing thread backtrace:

Thread 16 Crashed::  Dispatch queue: com.apple.coremedia.assetimagegenerator.notifications
0 libdispatch.dylib          … _dispatch_assert_queue_fail + 116
1 libdispatch.dylib          … dispatch_assert_queue + 188
2 libswift_Concurrency.dylib … swift_task_isCurrentExecutorImpl(swift::SerialExecutorRef) + 280
3 Migrate.debug.dylib        … closure #1 in ContentView.test() + 256
4 Migrate.debug.dylib        … thunk for @escaping @callee_guaranteed (@unowned CMTime, @guaranteed CGImageRef?…
5 AVFCore                    … -[AVAssetImageGenerator _didGenerateCGImage:] + 540
6 AVFCore                    … aig_didGenerateCGImage + 44
7 CoreFoundation             … __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 120

Frame 7 shows that this code is running in response to a notification being posted. Notifications like this run synchronously. This is on thread 16, so the poster posted the notification on thread 16. Obvious that’s not the main thread (-:

Frames 6 and 5 are the AVFoundation code routing the callback to you. Frameworks 4 and 3 is code in your app, but probably not you code. Rather, it’s glue generated by the compiler. That’s calling down into the Swift runtime, frames 2 through 0, to check the current executor context. And that check has failed, resulting in the trap you’re seeing.

In short, it’s crashing before it runs any code in your completion handler.

What should you do about this? This isn’t really my area of expertise, but I’ll note that the docs for the method you’re using say:

Swift clients should prefer the asynchronous images(for:) method instead.

So, I’m thinking that the best path forward here is for you to follow that advice and adopt the images(for:) method. I believe that’ll avoid this problem and yield nicer code!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Swift 6 AVAssetImageGenerator generateCGImagesAsynchronously
 
 
Q