How can I get rid of this warning for await exportSession.export()
:
Code:
func extractAudioFromImportedVideo(videoURL: URL, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) async {
do {
let asset = AVURLAsset(url: videoURL)
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
guard !audioTracks.isEmpty else {
failure(NSError(domain: "Audio Extraction Error", code: 0, userInfo: [NSLocalizedDescriptionKey: "Video does not contain audio"]))
return
}
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
failure(NSError(domain: "Audio Extraction Error", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create export session"]))
return
}
guard let audioURL = makeFileOutputURL(fileName: "ExtractedAudio.m4a") else {
failure(NSError(domain: "Audio Extraction Error", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create output URL"]))
return
}
print("audioTracks", audioTracks)
exportSession.outputURL = audioURL
exportSession.outputFileType = .m4a
await exportSession.export()
print("exportSession.status", exportSession.status.rawValue)
if exportSession.status == .completed {
// Audio extraction succeeded
print("Audio extraction succeeded")
success(audioURL)
} else {
// Audio extraction failed
print("Audio extraction failed: \(exportSession.error?.localizedDescription ?? "")")
failure(exportSession.error)
}
}
catch {
print("extractAudioFromImportedVideo Error:", error.localizedDescription)
}
}
The error is:
Passing argument of non-sendable type 'AVAssetExportSession' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
This is telling you that the problem is arising because you are performing this in a main actor-isolated function.
The easy fix is to remove this actor-isolation, to make the function nonisolated
:
nonisolated func extractAudioFromImportedVideo(videoURL: URL, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) async {
…
}
Personally, I would eliminate these closures and just follow async
patterns:
nonisolated func extractAudioFromImportedVideo(videoURL: URL) async throws -> URL {
do {
let asset = AVURLAsset(url: videoURL)
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
guard !audioTracks.isEmpty else {
throw AudioExtractionError.noAudio
}
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
throw AudioExtractionError.exportFailed
}
guard let audioURL = makeFileOutputURL(fileName: "ExtractedAudio.m4a") else {
throw AudioExtractionError.outputUrlFailed
}
print("audioTracks", audioTracks)
exportSession.outputURL = audioURL
exportSession.outputFileType = .m4a
await exportSession.export()
print("exportSession.status", exportSession.status.rawValue)
if let error = exportSession.error {
throw error
}
print("Audio extraction succeeded")
return audioURL
} catch {
print("extractAudioFromImportedVideo Error:", error.localizedDescription)
throw error
}
}
Note, I also retired NSError
, replacing it with:
enum AudioExtractionError: LocalizedError {
case noAudio
case exportFailed
case outputUrlFailed
nonisolated var errorDescription: String? {
return switch self {
case .noAudio: String(localized: "Video does not contain audio", comment: "AudioExtractionError")
case .exportFailed: String(localized: "Failed to create export session", comment: "AudioExtractionError")
case .outputUrlFailed: String(localized: "Failed to create output URL", comment: "AudioExtractionError")
}
}
}
That is actually localized, isolates the details from this function, and enumerated errors support richer error handling.
FWIW, once you have this working, you can simplify further:
nonisolated func extractAudioFromImportedVideo(videoURL: URL) async throws -> URL {
let asset = AVURLAsset(url: videoURL)
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
guard !audioTracks.isEmpty else {
throw AudioExtractionError.noAudio
}
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
throw AudioExtractionError.exportFailed
}
guard let audioURL = makeFileOutputURL(fileName: "ExtractedAudio.m4a") else {
throw AudioExtractionError.outputUrlFailed
}
exportSession.outputURL = audioURL
exportSession.outputFileType = .m4a
await exportSession.export()
if let error = exportSession.error {
throw error
}
return audioURL
}