Earlier this year, I was browsing through GSoC 2025 organizations. most of them had solid projects, but only a few really caught my attention. VideoLAN was one of them, and when I saw "VLC watchOS Port" on their project list, I knew I had to dig deeper.
I've been using VLC since I an the early days, the Window's built-in media player was useless for playing DVDrip pirated Sam Raimi's Spider-Man 2 or western TV series. Those files were mostly in different codecs (h.264 MP4 or MKV) with lebah ganteng's subs.
Before VLC, I used to install codecs manually, which was a total hassle! especially when I moved to other devices or when someone else yelled that they could play it on their PC but I couldn't.
VLC was literally the first app that taught me what "it just works" means. No codec hunting, no format complaints, just play the damn file straight. And this is probably my opportunity to contribute back to these open-source community project.
But here's the thing: the Apple Watch seemed like the perfect torture project for someone like me. Tiny screen so you have to be more considerate about Information Architecture? Check. Memory so limited you have to think twice about heap allocation? Absolutely. A battery that dies if you breathe on it wrong? You bet.
And then there's the fun part - making two devices talk to each other without losing their minds about who's in charge. When your phone says you're at track 3 but your watch thinks you're still on track 1, someone has to be the referee.
So naturally, I thought "yeah, this sounds fun" and decided to spend my summer on it.
The ports project isn't production-ready yet, but here's what I set out to achieve: a proper VLC media player for Apple Watch that would work in two modes:
- Companion mode: Your watch becomes a remote for the iOS VLC app. Skip tracks, adjust volume, browse your library - all from your wrist
- Standalone mode: Sync music/podcasts to your watch and listen completely independently. No phone required.
Key features that should covers:
- Plays pretty much any audio format VLC supports
- Syncs files from your iPhone using Apple's WatchConnectivity framework (WIP)
- Controls the playback from your wrist
- Digital Crown integration for seeking and volume (feels so natural once you try it) (WIP)
- Watch complications for quick access (WIP)
Let me be honest - this project was way more complex than my original proposal suggested. Working with a legacy codebase like VLC taught me that not every "best practice" you read online actually applies to real-world legacy software. Sometimes the "right" architectural pattern doesn't fit, and you have to make pragmatic decisions that balance idealism with what actually works within the existing system.
Here's what I learned the hard way:
Deep dive: Build system nightmares (WIP, need adjustment before making it publicly available)
Adding new scheme/target in VLC-ios build system to work with watchOS isn't easy as it sounds; it required significant adaptation. The existing scripts assumed iOS targets, and watchOS has different linking requirements and framework dependencies.
The challenging part was that figuring out why xcodebuild would succeed when building incrementally but fail completely on clean builds.
After extensive investigation, the root cause was that both iOS and watchOS CocoaPods were copying their XCFrameworks to the same destination path concurrently. When the build system ran in parallel, rsync operations would interfere with each other, corrupting the frameworks just as the Swift compiler tried to import them. The dependency management between iOS and watchOS pod targets was also incomplete, letting the build system run them simultaneously instead of respecting the platform ordering.
TL;DR: The deeper issue was in the Podfile's post_install configuration. The existing setup made pods support multiple platforms by setting generic SUPPORTED_PLATFORMS and TARGETED_DEVICE_FAMILY values. This caused build system confusion - iOS targets could incorrectly link to watchOS pods and vice versa, since VLCKit from the iOS pod appeared to support watchOS builds. The fix was making pod targets platform-specific by restricting their platform support settings.
See the artfacts table below for more details.
| Work Done / Artifacts | Status | Reference |
|---|
| Bootstrap WatchOS Target & shared Scheme | ✅ Merged | MR !1508 |
| watchOS Dependency Build Config | ✅ Merged | MR !1508 |
| CI Pipeline Setup | ✅ Merged | MR !1508 |
| Build system issue Technical Investigation | ✅ Resolved | Investigation Docs |
| watchOS shared-resource Foundation, headers, & Build Config | ✅ Merged | MR !1519 |
SwiftUI - The backward compatibility rabbit hole → (WIP, need attentions before making it publicly available)
Want to support older Apple Watch models? Hope you enjoy conditionally compiling UI components and dealing with APIs that changed between watchOS versions. SwiftUI is great until it isn't, and VLC needs to support users who don't upgrade their devices every year.
Targeting watchOS 9.0 means dealing with SwiftUI's rapidly evolving API surface. SwiftUI is fun until it isn't - Apple keeps introducing breaking changes with every major SDK version, and many useful features simply aren't available on watchOS 9.0.
You're constantly faced with the choice: settling for less functionality of SwiftUI features, abandoning certain UX features entirely, or diving into #if available hell where half your code becomes conditional compilation blocks. Want to use the latest navigation APIs? Too bad, they're watchOS 10+ only. Need proper list styling? Better check what's actually supported on your minimum target.
The result is code that looks like a compatibility minefield, where you spend more time checking availability than building actual features.
'Notes: Most of this work was put on hold since developing the UI separately doesn't scale well. Future work will likely revisit this UI implementation chunk by chunk, integrating it more closely with the core VLC architecture rather than building it as a standalone interface layer.
WatchConnectivity: Building reliable & idempotent sync infrastructure → (WIP, need adjustment before making it publicly available)
WatchConnectivity looks simple in the docs. In reality, it's a temperamental creature that randomly decides not to work, drops files mid-transfer, and has the error messages of a passive-aggressive roommate. It's not as straightforward as a typical Bluetooth connection where devices just pair and forget - WatchConnectivity has multiple connection states, session lifecycles, and reachability conditions that don't behave the way you'd expect.
After watching WWDC talks and digging through documentation, it became clear this was far more complex than initially anticipated. The initial bridge implementation had real problems: album covers would flicker, files dropped mid-transfer without any way to resume (even after downscaling images), and state inconsistencies created unpredictable behavior.
The core issue is that you have to be aware of connection state, message type priorities, and design methods to be idempotent regardless of message order. There's no silver bullet - you have to understand the state and effects for each message type individually. State conflict resolution between devices is another challenge entirely, but acknowledging these risks upfront is the first step toward building something that actually works reliably.
The initial bridge design wasn't scalable - event ordering could become inconsistent, leading to flaky flickering behavior. After diving deeper into WatchConnectivity, I realized I'd underestimated the complexity. Just the connection bridge itself ended up being a large diff, so I had to re-architect the entire approach.
Here's what I didn't expect: having an active session doesn't guarantee the watch is reachable, and isReachable behaves quite differently than isPaired. Also, watchOS doesn't establish connections like HTTP's three-way handshake or WebSocket protocols - it has its own session lifecycle that's more complex than the documentation suggests.
The solution became a notification-based connectivity bridge using Apple's WatchConnectivity framework with comprehensive infrastructure for both current POC capabilities and future production features. Think delegate-observer pattern with three core components handling session management, message routing, and UI updates across both platforms.
| Work Done / Artifacts | Status | Reference/Notes |
|---|
| watchOS Media Library & Playback Infrastructure | ✅ Merged | MR !1519 |
| watchOS AppCoordinator & ThumbnailCache | ✅ Merged | MR !1519 |
| WatchConnectivity Architecture Design | ✅ Complete | ADR-00X: VLC Watch Connectivity |
| Foundation Connection Bridge infrastructure | ⏸️ On reviews | MR !1531 *Splitted into 2 MRs for easier review |
| Now Playing UI & Playback Control Wiring | ⏸️ Planned | Depends on connection bridge MR |
| Payload Transfer with Chunking | ⏸️ Design Phase | >50MB files need chunking, no resume logic yet |
Architecture Note: The current implementation covers all 9 WCSessionDelegate methods and provides thread-safe notification dispatch, but lacks production-ready error handling and domain-specific VLC message protocols. The notification-based system allows easy addition of new observers and reactive UI integration for SwiftUI.
This project represents significant progress toward bringing VLC to Apple Watch. Let's just be real, it's not feature-complete or production-ready yet. Core infrastructure has been successfully merged to master: the build system now supports watchOS targets, foundational code enables media library and playback functionality on watchOS, and the connectivity architecture has been designed and documented.
The 12-week timeline proved challenging given the complexity of adapting legacy systems for watchOS constraints. Build system race conditions required deep investigation, SwiftUI backward compatibility created development overhead, and WatchConnectivity's session management proved more intricate than anticipated. These weren't roadblocks - they were learning opportunities that led to better architectural decisions.
The foundation is solid. Key infrastructure MRs have been merged, the connectivity bridge implementation is under review, and the path to standalone mode is clearly defined. This isn't about meeting an arbitrary deadline - it's about building something that works reliably in the real world.
But here's the thing: I'm committed to seeing this through. Extension or no extension, I'll keep working until everything I outlined in my original proposal is actually done. VLC deserves a proper watchOS app, and the foundation we've built is solid enough to get there.
The foundation is solid, but there's focused work ahead to complete the vision:
Solidify communication bridge: The WatchConnectivity integration needs refinement. Right now it works, but it's not as robust as it should be for daily use. Better error handling, connection recovery, and state synchronization between iOS and watchOS.
Enable offline & file sharing capability (standalone mode): This is the big one - making the watch truly independent. Users should be able to sync their media library and listen without their phone nearby. The storage management, efficient sync protocols, and local playback optimization work is partially there but needs completion.
These aren't nice-to-haves - they're core to what the VLC watchOS app should be. Once these pieces are solid, we'll have something that genuinely fills the gap in the Apple Watch ecosystem.
This project wouldn't exist without the mentors Felix Paul Kühne AND Diogo Simao Marques. They guided me through everything from debugging CI build issues, code reviews and giving a 2nd opinions.
They somehow managed to stay patient when I pushed dirty commit history with huge MRs (thousands of code changes) and even got warned about my daily update's communication style. Definitely wasn't the easiest contributors to work with.
Finally, thanks to Google Summer of Code for creating this program. The opportunity to spend a summer working on real open source projects with experienced mentors.
This post is part of my GSoC 2025 final report. You can read the detailed technical breakdowns in the linked posts above.