4.0 是 SwiftUI 重写版,从 3.x 升级需先看 MIGRATION.md;iOS 画中画需开启 Audio 后台模式并把音频会话类别设为 .playback;FairPlay 仅用假实现做过单元测试,需对接自己的 license 服务器验证

VersaPlayer
josejuanqmMIT活跃维护面向 iOS、macOS 和 tvOS 的可扩展视频播放器,基于 AVPlayer 封装,支持广告插入、AirPlay 和画中画等常见播放需求。
Versatile Video Player implementation for iOS, macOS, and tvOS
Swift Package Manager 添加:.package(url: "https://github.com/josejuanqm/VersaPlayer.git", from: "4.0.0")README 未说明;仅要求 iOS 17、tvOS 17、macOS 14、visionOS 1 及 Swift 6 系统环境
基于 AVFoundation 的 SwiftUI 视频播放器,跨 iOS、tvOS、macOS、visionOS,自带控件与自定义积木
面向 iOS、macOS 和 tvOS 的可扩展视频播放器,基于 AVPlayer 封装,支持广告插入、AirPlay 和画中画等常见播放需求。
核心能力
- 用 @Observable 的 VersaPlayerModel 驱动视图自动更新
- 提供 PlayPauseButton、Scrubber 等控件积木,可自由拼装
- 支持 HLS 自适应码率与 setQuality 峰值码率限制
- 内置 FairPlay DRM 与 ContentKeyProvider 自定义协议
- 支持画中画、AirPlay 及 tvOS 遥控器焦点导航
容易踩的坑
- 4.0.0VersaPlayer 4.0.0最新正式版2026-10-29 · 2026-10-29 05:42 UTC
4.0 rewrites VersaPlayer for SwiftUI. It's a breaking release: 3.x stays available at tag
3.0.2. See MIGRATION.md to upgrade..package(url: "https://github.com/josejuanqm/VersaPlayer.git", from: "4.0.0")What's new
- Requirements: iOS/tvOS 17, macOS 14, visionOS 1. Swift 6 with strict concurrency. Swift Package Manager only; CocoaPods is no longer supported.
VersaPlayerCore: an@ObservableVersaPlayerModelwrappingAVPlayer. It covers playback state, anAsyncStreamof events, error mapping, audio/subtitle tracks, caption styling, quality caps, FairPlay throughAVContentKeySession, Picture in Picture, plugins, and a built-inNowPlayingPluginfor the lock screen and remote commands.VersaPlayer(SwiftUI):VersaVideoPlayerwith default controls that hide on their own, plus building blocks for custom controls (PlayPauseButton,Scrubber,TrackPicker,PiPButton,AirPlayButton, …).VersaPlayerViewControllerwraps it for UIKit and AppKit apps.- tvOS: Siri Remote support: the seek bar scrubs, and the track and Picture in Picture menus can take focus.
- Demo: one XcodeGen app for all four platforms (
Examples/VersaPlayerDemo).
- 3.0.0Version 3.0.02019-07-04 · 2019-07-04 07:29 UTC
查看更新说明
Carthage support.
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
News
:tada: VersaPlayer 4.0 is a SwiftUI rewrite. Coming from 3.x? See MIGRATION.md.
VersaPlayer is a SwiftUI video player built on AVFoundation: an observable player model, a ready-made controls overlay, and building blocks for your own controls. Supports HLS, tracks, caption styling, FairPlay, Picture in Picture and AirPlay on iOS, tvOS, macOS and visionOS.
Installation
Swift Package Manager:
.package(url: "https://github.com/josejuanqm/VersaPlayer.git", from: "4.0.0")
Or in Xcode: File › Add Package Dependencies… and paste the repository URL.
Usage
Basic
import SwiftUI
import VersaPlayer
struct PlayerScreen: View {
@State private var model = VersaPlayerModel()
var body: some View {
VersaVideoPlayer(model: model)
.aspectRatio(16 / 9, contentMode: .fit)
.task { model.load(VersaPlayerItem(url: url)) }
}
}
load autoplays by default. Pass autoplay: false to start paused.
State and events
VersaPlayerModel is @Observable, so views update on their own:
model.isPlaying, model.isBuffering, model.currentTime, model.duration, model.isLive, model.status
For one-off events (ready, ended, failed, ...):
.task {
for await event in model.events {
if case .failed(let error) = event { print(error) }
}
}
To block playback (for example, to show an ad first):
model.shouldBeginPlayback = { _ in
await ads.playPreRoll()
return true
}
Custom controls
Pass any view as controls. Use the building blocks, which read the player from the environment:
VersaVideoPlayer(model: model) {
VStack {
Spacer()
HStack {
PlayPauseButton()
Scrubber()
TimeLabel(.remaining)
TrackPicker(.legible)
}
.padding()
}
}
Building blocks: PlayPauseButton, SkipButton(seconds:), RateButton(rate:), Scrubber, TimeLabel, BufferingIndicator, TrackPicker, PiPButton, AirPlayButton, FullscreenButton.
For video without any overlay, use VideoSurface(model:).
Options
VersaVideoPlayer(model: model)
.videoGravity(.resizeAspectFill)
.controlsAutohide(after: .seconds(5)) // nil keeps controls visible
.doubleTapTogglesGravity(false)
Fullscreen
The player asks your app to toggle fullscreen; your app presents it. The fullscreen button appears once you register the request:
@State private var isFullscreen = false
VersaVideoPlayer(model: model)
.onFullscreenRequest(isFullscreen: false) { isFullscreen = true }
.fullScreenCover(isPresented: $isFullscreen) {
VersaVideoPlayer(model: model)
.onFullscreenRequest(isFullscreen: true) { isFullscreen = false }
.ignoresSafeArea()
}
Tracks
model.tracks(of: .audio) // [MediaTrack]
model.tracks(of: .legible) // subtitles and captions
model.select(track)
model.deselect(.legible) // subtitles off
model.selectedTracks[.audio]
For adaptive (HLS) quality caps:
model.setQuality(QualityPreference(peakBitRate: 2_000_000))
Caption styling
var style = CaptionStyle()
style.foregroundColor = .init(red: 1, green: 0.85, blue: 0)
style.relativeFontSize = 150
style.edgeStyle = .dropShadow
model.setCaptionStyle(style)
FairPlay
let provider = FairPlayHTTPProvider(
certificateURL: URL(string: "https://example.com/fps/cert")!,
licenseURL: URL(string: "https://example.com/fps/license")!
)
model.load(VersaPlayerItem(url: url, drm: DRMConfiguration(provider: provider)))
For a custom license protocol, implement ContentKeyProvider. The FairPlay flow is unit tested with fakes only; test it against your own license server.
tvOS
The default controls work with the Siri Remote. While the controls are hidden, select shows them, left/right skips 10 seconds, and play/pause toggles playback. While they're visible, the seek bar has focus: left/right scrubs (faster with quick repeated presses), and select jumps there. Swipe up for audio, subtitles and Picture in Picture. Menu hides the controls.
Custom controls with focusable buttons should add .controlsTakeFocus(), so focus moves into them while they're visible.
Picture in Picture and AirPlay
PiPButton and AirPlayButton are in the default controls. For Picture in Picture on iOS, enable the Audio background mode and set the audio session category to .playback.
Plugins
final class Analytics: VersaPlayerPlugin {
func attach(to model: VersaPlayerModel) {
Task { for await event in model.events { track(event) } }
}
}
model.add(Analytics())
Built in: NowPlayingPlugin shows the title, artwork and progress on the lock screen and in Control Center, and handles remote play/pause/skip/seek. It uses VersaPlayerItem.metadata:
model.add(NowPlayingPlugin())
model.load(VersaPlayerItem(url: url, metadata: .init(title: "Episode 1", subtitle: "My Show", artworkURL: artwork)))
UIKit / AppKit
let controller = VersaPlayerViewController()
controller.model.load(VersaPlayerItem(url: url))
Example
brew install xcodegen
cd Examples/VersaPlayerDemo && xcodegen generate && open VersaPlayerDemo.xcodeproj
One app target for iOS, tvOS, macOS and visionOS.
Author
Jose Quintero - [email protected]
Star History
Contributors
People that make VersaPlayer possible, Thank you!
Donation
If you like this project or has been helpful to you, you can buy me a cup of coffe :) Appreciate it!
License
VersaPlayer is available under the MIT license. See the LICENSE file for more info.
