# How Vorssaint-utils Implements Now Playing Media Controls on macOS

> Discover how Vorssaint-utils implements Now Playing media controls on macOS by using an out-of-process bridge to bypass code signature restrictions on the private MediaRemote framework.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: internals
- Published: 2026-09-12

---

**Vorssaint-utils implements Now Playing media controls through an out-of-process bridge that combines a Swift-compiled dynamic library, a Perl wrapper, and a bridge runner to circumvent macOS 15.4+ code signature restrictions on the private MediaRemote framework.**

Vorssaint-utils is an open-source macOS utility that displays current media playback information through its radial menu interface. Because macOS 15.4 and later restrict direct access to the MediaRemote framework to Apple-signed processes only, the project employs a sophisticated architectural workaround to capture real-time Now Playing data. This article examines the complete implementation across the adapter library, Perl bridge, and SwiftUI service layers.

## The macOS 15.4+ Signature Restriction

Apple's `MediaRemote` framework is a private API that provides access to system-wide media playback information. Starting with macOS 15.4, the framework enforces code signature validation, returning data only to processes signed by Apple. Since Vorssaint-utils cannot carry Apple's code signature, it cannot link against `MediaRemote` directly. Instead, the project uses an **out-of-process bridge** architecture that isolates the sensitive framework calls into a separately compiled dynamic library.

## Architecture Overview

The Now Playing implementation follows a multi-stage pipeline to transport data from the private framework to the SwiftUI interface:

```

RadialNowPlayingService → MediaRemoteNowPlayingBridge → /usr/bin/perl → now-playing.pl → libVorssaintNowPlaying.dylib → MediaRemote framework → JSON → RadialNowPlayingSnapshot → UI

```

This flow circumvents the code signature restriction by executing the framework-calling code in a separate process while maintaining clean data transfer via JSON.

## The Adapter Library (libVorssaintNowPlaying.dylib)

The core of the workaround resides in [`Sources/NowPlayingAdapter/NowPlayingAdapter.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/NowPlayingAdapter/NowPlayingAdapter.swift), which compiles into `libVorssaintNowPlaying.dylib`. This dynamic library directly invokes private `MediaRemote` C-functions and exposes a C entry point for external callers.

### Loading Private Framework Symbols

The library uses `dlopen` to load the `MediaRemote` framework at runtime and `dlsym` to resolve function symbols. A generic helper function handles this dynamic linking:

```swift
// Lines 33-36 in NowPlayingAdapter.swift
func function<T>(_ name: String) -> T {
    // dlopen MediaRemote.framework and dlsym the symbol
}

```

### Gathering Now Playing Data

The `vorssaintNowPlayingGet` function (lines 44-111) orchestrates data collection using `MRMediaRemoteGetNowPlayingInfo`, `MRMediaRemoteGetNowPlayingApplicationPID`, and related callbacks. It utilizes a dispatch group to synchronize asynchronous metadata retrieval, collecting the title, artist, album, playback rate, optional artwork (base-64-encoded), PID, display ID, and playing state.

### JSON Serialization

Once collected, the metadata dictionary is serialized to a single-line JSON string printed to stdout via the `emit` helper (lines 38-42). This format ensures safe, parseable data transfer between the Perl wrapper and the Swift bridge.

## The Perl Bridge Layer

A Perl script located at [`Resources/now-playing.pl`](https://github.com/vorssaint/vorssaint-utils/blob/main/Resources/now-playing.pl) serves as the intermediary between the compiled library and the main application. The script uses `DynaLoader` to load `libVorssaintNowPlaying.dylib` at runtime and exposes the C function `vorssaint_now_playing_get`. When invoked, it triggers the Swift code, captures the JSON output line, and forwards it to stdout for the bridge runner to consume.

## The Bridge Runner (MediaRemoteNowPlayingBridge)

Within [`Sources/Vorssaint/Services/RadialMenu/RadialNowPlayingService.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/Services/RadialMenu/RadialNowPlayingService.swift), the `MediaRemoteNowPlayingBridge` class manages the out-of-process execution. It launches `/usr/bin/perl` with the script path and compiled library location, enforces a strict 2-second timeout to prevent hanging, and parses the returned JSON into a `RadialNowPlayingSnapshot` struct (lines 95-108).

**Key implementation details:**
- **Process spawning**: Uses `Process` API to execute the Perl interpreter
- **Timeout handling**: Terminates the process if it exceeds 2 seconds
- **JSON parsing**: Converts the single-line JSON output into a strongly-typed Swift struct

## Service and UI Integration

### RadialNowPlayingService Singleton

The `RadialNowPlayingService` acts as the primary interface for the UI layer. As a singleton (lines 12-41), it requests fresh snapshots from the bridge, caches results, and drives the `RadialNowPlayingCard` SwiftUI view. The service implements state management through a `refresh` method (lines 62-91) that handles loading states, successful playback detection, and "nothing playing" scenarios.

### Opening the Source Application

When users click the Now Playing card, the service resolves the originating application via bundle ID or PID and opens it using `RadialNowPlayingApplication.open(snapshot)` (lines 124-132, 136-188). This creates a seamless workflow where users can jump directly to the media source from the radial menu.

## Implementation Code Examples

Fetching a snapshot manually using the bridge:

```swift
let bridge = MediaRemoteNowPlayingBridge()
bridge.fetch { snapshot in
    if let s = snapshot {
        print("Now playing: \(s.title ?? "-") by \(s.artist ?? "-")")
    } else {
        print("No media playing")
    }
}

```

Using the high-level service as implemented in the application:

```swift
RadialNowPlayingService.shared.refresh { state in
    switch state {
    case .playing(let snapshot):
        print("Now playing:", snapshot.title ?? "-")
    case .loading:
        print("Loading now-playing info…")
    case .nothingPlaying:
        print("Nothing is playing")
    }
}

```

Opening the originating application from the UI card:

```swift
RadialNowPlayingApplication.open(snapshot)

```

## Summary

- **Signature workaround**: Vorssaint-utils cannot access `MediaRemote` directly due to macOS 15.4+ restrictions, so it uses an out-of-process bridge architecture.
- **Dynamic library**: `libVorssaintNowPlaying.dylib` (built from [`NowPlayingAdapter.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/NowPlayingAdapter.swift)) handles raw `MediaRemote` framework calls and emits JSON metadata.
- **Perl wrapper**: [`Resources/now-playing.pl`](https://github.com/vorssaint/vorssaint-utils/blob/main/Resources/now-playing.pl) loads the dynamic library via `DynaLoader` and forwards output to the bridge runner.
- **Bridge execution**: `MediaRemoteNowPlayingBridge` spawns the Perl process with a 2-second timeout and parses the resulting `RadialNowPlayingSnapshot`.
- **UI integration**: `RadialNowPlayingService` manages state, caching, and presentation through `RadialNowPlayingCard`, plus application launching via `RadialNowPlayingApplication`.

## Frequently Asked Questions

### Why doesn't Vorssaint-utils access MediaRemote directly?

macOS 15.4 and later require Apple code signatures to receive data from the private `MediaRemote` framework. As a third-party application, Vorssaint-utils carries a different code signature, causing the framework to return empty results. The out-of-process bridge isolates the framework calls into a separately compiled component that runs under the required constraints.

### What data fields are available in the Now Playing snapshot?

The `RadialNowPlayingSnapshot` struct contains the track title, artist name, album name, current playback rate, optional artwork data (base-64-encoded), the originating application's PID, the display ID where playback is occurring, and a boolean indicating whether media is currently playing.

### How does the bridge handle cases where no media is playing?

When `MRMediaRemoteGetNowPlayingInfo` returns no data, the adapter library emits a JSON structure indicating the absence of playback. The `MediaRemoteNowPlayingBridge` parses this as a nil or empty snapshot, which `RadialNowPlayingService` translates into the `.nothingPlaying` state for UI presentation.

### Is the dynamic library signed differently than the main application?

The `libVorssaintNowPlaying.dylib` is built and signed independently from the main Vorssaint-utils binary. This separation is crucial because it allows the library to be loaded by the Perl interpreter (a system process with distinct entitlements) while still executing code that interfaces with the restricted `MediaRemote` framework.