A Swift wrapper around libVLC for iOS, macOS, tvOS, and Mac Catalyst.
Apple's AVFoundation covers a narrow slice of the media landscape. It cannot play MKV, FLAC, or most subtitle formats, and it does not support network protocols like RTSP, SMB, or UPnP. Codec support is limited to what Apple ships. Any app that needs to reach beyond MP4 and HLS eventually runs out of runway.
VLC plays virtually everything, and its engine, libVLC, is available as a C library you can embed in any app.
The existing iOS wrapper, VLCKit, is written in Objective-C. It uses delegates, KVO, NSNotificationCenter, and manual thread management, which is a faithful reflection of the era it was designed in.
SwiftVLC wraps libVLC 4.0 directly in Swift, with no Objective-C layer in between. It is built for @Observable, async/await, and VideoView(player).
| SwiftVLC | VLCKit | |
|---|---|---|
| Language | Swift 6 | Objective-C |
| Bindings | Direct C → Swift | C → Objective-C → Swift bridging |
| State management | @Observable, drives SwiftUI directly |
KVO, NSNotificationCenter, and delegates |
| Concurrency | @MainActor, Sendable, async/await |
Manual thread dispatch, no isolation |
| Video rendering | VideoView(player) |
Manual UIView setup plus drawable configuration |
| Errors | throws(VLCError), typed and exhaustive |
NSError codes |
| Events | AsyncStream<PlayerEvent> with multiple consumers |
NSNotificationCenter |
| libVLC version | 4.0 | 3.x |
| PiP | Built in via the vmem pipeline | Not included |
| Swift 6 safe | Yes, with strict concurrency | No |
@Observableplayer: state, current time, duration, tracks, and volume drive SwiftUI directly.VideoView(player)handles the rendering lifecycle in a single SwiftUI view.- Typed errors via
throws(VLCError)instead of error codes. - Asynchronous media parsing:
try await media.parse()with cancellation support. - 10-band equalizer with libVLC's built-in presets.
- A-B looping, playback rate control, and subtitle and audio delay.
- Picture-in-Picture with full playback controls.
- Network discovery for LAN, SMB, UPnP media sources, and Chromecast and AirPlay renderers.
- 360° video with full viewpoint control over yaw, pitch, roll, and field of view.
- Asynchronous thumbnail generation at arbitrary timestamps.
MediaListPlayerfor playlist playback with loop and repeat modes.
- Swift 6.3+ / Xcode 26+
- iOS 18+ / macOS 15+ / tvOS 18+ / Mac Catalyst 18+
In Xcode, choose File → Add Package Dependencies, paste the repo URL, and Xcode will pick up the latest release automatically:
https://github.com/harflabs/SwiftVLC.git
From a Package.swift manifest, add a dependency and pin to the
current release. The version string lives on the
releases page.
.package(url: "https://github.com/harflabs/SwiftVLC.git", from: "x.y.z")The pre-built libVLC xcframework downloads automatically via SPM. It's a large binary (multi-GB unstripped; the release zip is a few hundred MB).
import SwiftUI
import SwiftVLC
struct PlayerView: View {
@State private var player = Player()
var body: some View {
VideoView(player)
.onAppear {
try? player.play(url: URL(string: "https://example.com/video.mp4")!)
}
}
}// Playback
let player = Player()
try player.play(url: videoURL)
player.pause()
player.stop()
player.position = 0.5 // Seek to 50%
player.rate = 1.5 // 1.5x speed
player.volume = 0.8 // 80% volume
player.isMuted = true
// Tracks
player.selectedSubtitleTrack = player.subtitleTracks[1]
// Metadata
let media = try Media(url: videoURL)
let metadata = try await media.parse()
print(metadata.title, metadata.duration)
// Events
for await event in player.events {
switch event {
case .stateChanged(let state): ...
case .timeChanged(let time): ...
default: break
}
}Full API reference is hosted on Swift Package Index: swiftpackageindex.com/harflabs/swiftvlc/documentation
The Showcase/ directory contains a full-featured demo app for each supported platform:
- iOS. Tap-to-show controls, swipe gestures, equalizer, and PiP.
- macOS. Hover controls, keyboard shortcuts, and a floating settings panel.
- tvOS. A 10-foot UI with Siri Remote navigation and swipe-to-scrub.
- Mac Catalyst. The iOS player running natively on Mac.
A comprehensive Swift Testing suite covers every public API. There is no XCTest and no mocks: every test runs against the real libVLC binary, so regressions in the C bridge surface immediately rather than hiding behind a fake. CI runs the full suite on every push and every pull request.
swift testSee ARCHITECTURE.md for test tags, fixtures, and structure.
git clone https://github.com/harflabs/SwiftVLC.git
cd SwiftVLC
./scripts/setup-dev.sh
swift testsetup-dev.sh downloads libvlc.xcframework.zip from the latest release into Vendor/. Package.swift on every branch points at that local path, so no manifest edits are needed before building.
setup-dev.sh flag |
Effect |
|---|---|
| (none) | Download the latest release if Vendor/ is empty; otherwise keep existing. |
vX.Y.Z (positional) |
Pin to a specific release tag. |
--force |
Re-download even if Vendor/ already exists. |
--skip-download |
Only flip Package.swift to local path. Expects Vendor/ to already exist, which is useful after running build-libvlc.sh. |
Needed only when bumping VLC_HASH, modifying build patches, or preparing a release. Day-to-day Swift development doesn't require it.
brew install autoconf automake libtool cmake pkg-config gettext
./scripts/build-libvlc.sh --allExpect ~15–20 minutes on a clean run and ~2–7 minutes warm on Apple Silicon. The script clones VLC at a pinned commit into scripts/.build-libvlc/, applies the source patches below, builds every contrib (FFmpeg, dav1d, x264, libass, …) per slice, and assembles the result into Vendor/libvlc.xcframework.
| Flag | Platforms |
|---|---|
| (default) | iOS device + simulator |
--all |
iOS, tvOS, macOS, Mac Catalyst (six slices) |
--ios-only / --tvos-only / --macos-only / --catalyst-only |
Replaces Vendor/ with that single platform |
--tvos / --macos / --catalyst |
Adds a platform to the default set |
--clean / --clean-build |
Wipe scripts/.build-libvlc/ (the latter rebuilds afterwards) |
--hash=<sha> |
Override the pinned VLC commit |
*-onlyflags replace the xcframework; any slices already inVendor/are lost.
VLC master doesn't build cleanly against current Xcode and Homebrew libtool. The script applies these patches in-tree on every invocation, idempotently:
- Mac Catalyst. Teaches VLC's build system the
macabitarget triple and guards OpenGLES-only code paths. - Xcode 26 LDFLAGS. Adds
-isysrootto linker invocations so libSystem resolves. - libtool 2.5 OBJC tag. Adds
_LIBTOOLFLAGS = --tag=CCto theMakefile.amfiles that contain.msources. Older libtool versions inferred the tag; 2.5 refuses. - Rust contribs disabled.
cargo-c 0.9.29no longer compiles on recent Rust. The only Rust contrib on Apple israv1e(AV1 encoder);dav1dhandles decoding. dup3/pipe2. Forced unavailable via autoconf cache vars. iOS Simulator SDK 26 exports these Linux-only syscalls from libSystem, fooling configure into using them.
git reset --hard only runs when HEAD is not at VLC_HASH, so the patches and per-platform build dirs survive repeated runs.
Releases use a tag-only model: the commit that pins Package.swift to the remote xcframework URL exists only under the tag, never on a branch. Every branch stays ready for setup-dev.sh && swift build.
./scripts/build-libvlc.sh --all # produces Vendor/libvlc.xcframework
./scripts/release.sh X.Y.Z --dry-run # strip + zip + checksum, no push
./scripts/release.sh X.Y.Z # cut the releaseWhat release.sh does:
- Verifies all six platform slices are present in the xcframework.
- Copies it to a temp dir, strips debug symbols, zips with
ditto. - Computes SHA-256 via
swift package compute-checksum. - Creates a detached commit with
Package.swiftrewritten to the remote URL and checksum. - Tags that commit as
vX.Y.Z. - Resets the branch back to its previous HEAD; the commit survives only as the tag.
- Pushes the tag (never the branch).
- Uploads the zip to a new GitHub Release.
Preflight refuses non-main branches (--allow-dirty-branch to override), pre-existing local tags, and unauthenticated gh. If any later step fails, the EXIT trap resets the branch to its pre-release HEAD so nothing is left dangling.
For internals, including module design, C interop, the concurrency model, the event system, memory management, and the PiP rendering pipeline, see ARCHITECTURE.md.
MIT. See LICENSE.
libVLC is licensed under LGPLv2.1. Static linking may have licensing implications. See the VLC licensing FAQ.
SwiftVLC stands on the work of the VideoLAN community. The VLC media player and libVLC are among the most important open-source projects in media, representing decades of work by hundreds of contributors that made it possible to play virtually anything, anywhere.
Thanks also to VLCKit for paving the way for libVLC on Apple platforms. VLCKit proved that embedding VLC in iOS and macOS apps was not only possible but practical, and it has powered countless apps over the years. SwiftVLC would not exist without the foundation VLCKit laid.