Last active
June 24, 2024 15:20
-
-
Save samsonjs/62bf98437627d67d63b5eff99eb6bd77 to your computer and use it in GitHub Desktop.
FB14035001: Tracks loaded from an AVAsset should be marked `sending` to be used from actor-isolated contexts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AVFoundation | |
/* | |
* In Swift 6 it’s not currently possible to directly load tracks from an AVAsset using a method like | |
* AVAsset.loadTracks(withMediaType:) from an actor-isolated context, because AVAssetTrack isn’t Sendable. Given | |
* that many AVFoundation types are not sendable / thread-safe it’s tempting to use an actor to work with | |
* compositions and that currently requires some work-arounds that shouldn’t be necessary and may not be obvious | |
* to everyone. | |
*/ | |
// Non-isolated contexts work fine but if this class were building an AVComposition then it’s preferable to enforce | |
// isolation / serialization of access to the components of that composition. | |
class NonisolatedTrackLoader { | |
func loadTracks() async throws { | |
// ✅ Works in nonisolated contexts | |
try await AVAsset().loadTracks(withMediaType: .video) | |
} | |
} | |
// Since many AVFoundation types aren’t Sendable it’s going to be common to reach for actors to work with them. | |
// However this is currently a warning in Swift 5 and an error in Swift 6. | |
actor IsolatedTrackLoader { | |
func loadTracks() async throws { | |
// ❌ Non-sendable type '[AVAssetTrack]' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary. Class 'AVAssetTrack' does not conform to the 'Sendable' protocol | |
try await AVAsset().loadTracks(withMediaType: .video) | |
} | |
// Using the extension method below we can load tracks because they’re marked as being sent, which should be the default | |
func loadTracksWithWorkaround() async throws { | |
// ✅ Works when tracks are sent | |
_ = try await AVAsset().sendTracks(withMediaType: .video) | |
} | |
} | |
extension AVAsset { | |
// This works around the issue but we shouldn't need to write this ourselves. The built-in methods for loading | |
// tracks should send them to the caller. | |
func sendTracks(withMediaType mediaType: AVMediaType) async throws -> sending [AVAssetTrack] { | |
try await loadTracks(withMediaType: mediaType) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment