[video_player] Convert top-level classes to Swift#11658
[video_player] Convert top-level classes to Swift#11658auto-submit[bot] merged 22 commits intoflutter:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request converts the core native implementation of the video_player_avfoundation package from Objective-C to Swift, including the main plugin class and platform view factory. It also refactors the Pigeon API definitions into separate instance and plugin-level files. Feedback identifies a critical runtime crash risk in VideoPlayerPlugin.swift due to mutating the playersByIdentifier dictionary during iteration. Additionally, a safety concern was raised regarding the force-unwrapping of player lookups in NativeVideoViewFactory.swift, which could lead to crashes if an invalid ID is provided.
I am having trouble creating individual review comments. Click here to see my feedback.
packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/VideoPlayerPlugin.swift (159)
Iterating directly over playersByIdentifier.values while calling disposeWithError is unsafe because disposeWithError triggers the onDisposed callback, which removes the player from the dictionary. Mutating a dictionary during iteration causes a runtime crash in Swift. You should iterate over a copy of the values, as was done in the original Objective-C implementation.
for player in Array(playersByIdentifier.values) {
packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/NativeVideoViewFactory.swift (63-64)
Force unwrapping the result of playerByIdentifierProvider will cause a crash if the playerId is not found in the registry. While the Dart side is expected to provide valid IDs, it's safer to handle a missing player gracefully to avoid crashing the host application. Returning a view with a default AVPlayer is a safer fallback than a runtime crash.
guard let player = playerByIdentifierProvider(args.playerId) else {
return FVPNativeVideoView(player: AVPlayer())
}
return FVPNativeVideoView(player: player.player)
I researched this when I was locally reviewing the Gemini-created migration, because I was surprised that it had removed the copy, and from what I found it is safe. The Gemini comment here is misleading; based on what I found, the code here is not actually mutating the dictionary during iteration of the dictionary, but instead during iteration of an independent view. |
| } | ||
|
|
||
| @Test func playerForPlatformViewDoesNotRegisterTexture() { | ||
| @Test func playerForPlatformViewDoesNotRegisterTexture() throws { |
|
|
||
| /// A factory class responsible for creating native video views that can be embedded in a | ||
| /// Flutter app. | ||
| class NativeVideoViewFactory: NSObject, FlutterPlatformViewFactory { |
There was a problem hiding this comment.
nit: final
is NSObject required?
There was a problem hiding this comment.
nit:
final
Done.
is NSObject required?
Yes; you can't have a Swift class that implements an Obj-C protocol without also inheriting from NSObject (it's a compiler error).
|
|
||
| #if os(macOS) | ||
| func create( | ||
| withViewIdentifier viewId: Int64, |
There was a problem hiding this comment.
should we be consistent with either using identifier or ID?
There was a problem hiding this comment.
I changed it to viewIdentifier. Sadly this poor Obj-C variable naming was straight from our header.
| } | ||
| #endif | ||
|
|
||
| func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { |
There was a problem hiding this comment.
maybe just argsCodec as it doesn't actually create
There was a problem hiding this comment.
I think many of the engine's Obj-C APIs are poorly named, but changing them is difficult. If the iOS team is willing to maintain multiple versions of a bunch of APIs for a long time horizon, so that we can eventually move toward better names, I'd definitely be on board with that, it's just not been something I've had a chance to prioritize.
| with: FVPCreationOptions.make(withUri: "", httpHeaders: [:]), error: &error) | ||
| #expect(error == nil) | ||
| let player = videoPlayerPlugin.playersByIdentifier[identifiers!.playerId] as! FVPVideoPlayer | ||
| // Provide a URI that is a valid URI, but not a video, to trigger a failure inside AVPlayer. |
There was a problem hiding this comment.
Unlike the other changes in this file, this isn't just a rote structural change for the switch from testing a Obj-C-Pigeon-generated API to a Swift-Pigeon-generated API. The old test used "" as the URI, but that actually ends up creating a nil NSURL, which fails earlier than what this test needs now because we aren't internally passing along a nil NSURL on accident.
The goal of this test is to test an AVPlayer that fails to load a video, and for that we need a valid URI.
| } | ||
| #endif | ||
|
|
||
| func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { |
There was a problem hiding this comment.
I think many of the engine's Obj-C APIs are poorly named, but changing them is difficult. If the iOS team is willing to maintain multiple versions of a bunch of APIs for a long time horizon, so that we can eventually move toward better names, I'd definitely be on board with that, it's just not been something I've had a chance to prioritize.
|
|
||
| /// A factory class responsible for creating native video views that can be embedded in a | ||
| /// Flutter app. | ||
| class NativeVideoViewFactory: NSObject, FlutterPlatformViewFactory { |
There was a problem hiding this comment.
nit:
final
Done.
is NSObject required?
Yes; you can't have a Swift class that implements an Obj-C protocol without also inheriting from NSObject (it's a compiler error).
|
|
||
| #if os(macOS) | ||
| func create( | ||
| withViewIdentifier viewId: Int64, |
There was a problem hiding this comment.
I changed it to viewIdentifier. Sadly this poor Obj-C variable naming was straight from our header.
|
autosubmit label was removed for flutter/packages/11658, because - The status or check suite Linux repo_checks has failed. Please fix the issues identified (or deflake) before re-applying this label. |
…#186256) flutter/packages@0411f1d...92552b1 2026-05-07 stuartmorgan@google.com [video_player] Convert top-level classes to Swift (flutter/packages#11658) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC flutter-ecosystem@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Converts the top-level plugin class, along with the native view factory that is used for platform view management, from Objective-C to Swift. The Pigeon interface for the plugin class has been split out into a separate definition file so that it can use the Swift Pigeon generator while the player-instance-level API can continue to use Obj-C for now.
Since this is the first use of Swift in the plugin, this includes separating the Swift Package into more modules; now instead of:
video_player_avfoundation (Obj-C) -> video_player_avfoundation_ios and _macos (both Obj-C)
it's
video_player_avfoundation (Swift) -> video_player_avfoundation_objc (Obj-C) -> video_player_avfoundation_ios and _macos (both Obj-C)
The Swift conversions were done via Gemini as a starting point, but I did a side-by-side comparison to the Obj-C to ensure that everything was actually preserved, and fixed issues in its conversion manually.
The PR also includes adding some NONNULL region annotations to Obj-C headers that were accidentally missing it, since I ran into one of the omissions once I was calling the API from Swift, and then I audited all of the headers.
Part of flutter/flutter#119105
Pre-Review Checklist
[shared_preferences]///).Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2