Skip to content

[video_player] Convert top-level classes to Swift#11658

Merged
auto-submit[bot] merged 22 commits intoflutter:mainfrom
stuartmorgan-g:video-player-swift-pigeon
May 7, 2026
Merged

[video_player] Convert top-level classes to Swift#11658
auto-submit[bot] merged 22 commits intoflutter:mainfrom
stuartmorgan-g:video-player-swift-pigeon

Conversation

@stuartmorgan-g
Copy link
Copy Markdown
Collaborator

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

Footnotes

  1. 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

@github-actions github-actions Bot removed the CICD Run CI/CD label May 6, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

critical

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)

high

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)

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 6, 2026
@stuartmorgan-g
Copy link
Copy Markdown
Collaborator Author

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.

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.

@github-actions github-actions Bot removed the CICD Run CI/CD label May 6, 2026
@stuartmorgan-g stuartmorgan-g added the CICD Run CI/CD label May 6, 2026
Copy link
Copy Markdown
Contributor

@hellohuanlin hellohuanlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

}

@Test func playerForPlatformViewDoesNotRegisterTexture() {
@Test func playerForPlatformViewDoesNotRegisterTexture() throws {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!


/// A factory class responsible for creating native video views that can be embedded in a
/// Flutter app.
class NativeVideoViewFactory: NSObject, FlutterPlatformViewFactory {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: final
is NSObject required?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: viewID

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we be consistent with either using identifier or ID?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to viewIdentifier. Sadly this poor Obj-C variable naming was straight from our header.

}
#endif

func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe just argsCodec as it doesn't actually create

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to viewIdentifier. Sadly this poor Obj-C variable naming was straight from our header.

@github-actions github-actions Bot removed the CICD Run CI/CD label May 7, 2026
@stuartmorgan-g stuartmorgan-g added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels May 7, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 7, 2026
@auto-submit
Copy link
Copy Markdown
Contributor

auto-submit Bot commented May 7, 2026

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.

@github-actions github-actions Bot removed the CICD Run CI/CD label May 7, 2026
@stuartmorgan-g stuartmorgan-g added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels May 7, 2026
@auto-submit auto-submit Bot merged commit 92552b1 into flutter:main May 7, 2026
83 checks passed
pull Bot pushed a commit to Mattlk13/flutter that referenced this pull request May 8, 2026
…#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: video_player platform-ios platform-macos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants