The Problem

Streaming a macOS screen to another Apple device normally means either Apple's own AirPlay (which is locked down and not embeddable) or a third-party service that routes traffic through a relay server. MirageKit solves the embeddable, peer-to-peer problem: a macOS host captures windows or a virtual display, encodes HEVC, and pushes frames over UDP directly to a client on iPadOS, visionOS, or macOS — no server in the path.

What This Does

MirageKit is a Swift Package that ships both sides of the stream. The host side (Sources/MirageKit/Public/Host/) captures via ScreenCaptureKit, encodes with HEVCEncoder.swift, and advertises over Bonjour (BonjourAdvertiser.swift). The client side (Sources/MirageKit/Public/Client/) discovers hosts, decodes with HEVCDecoder.swift, and renders through MetalRenderer.swift inside SwiftUI views (MirageStreamView.swift, MirageStreamContentView.swift).

Input is forwarded back: MirageHostInputController.swift and the Public/Input/ types (MirageMouseEvent.swift, MirageKeyEvent.swift) carry mouse, keyboard, and gesture events from client to host. The framework also handles virtual displays (CGVirtualDisplayBridge.swift), remote unlock (UnlockManager.swift), and menu-bar passthrough (MenuBarMonitor.swift).

How It Is Wired

The static import graph resolved 0 internal modules and 0 import edges across 76 files, so the module wiring has not been mapped for this repository yet — the flow below is traced from the README and file structure, not from a resolved call graph.

Execution starts at MirageHostService.start() (Sources/MirageKit/Public/Host/MirageHostService.swift). From there the host advertises _mirage._tcp via Bonjour, accepts TCP control connections, and streams video over UDP with clients registering stream IDs. The client entry point is MirageClientService.connect(to:) (Sources/MirageKit/Public/Client/MirageClientService.swift). The network boundary is crossed in HybridTransport.swift and ConnectionManager.swift (TCP control + UDP video); the capture boundary in WindowCaptureEngine.swift (ScreenCaptureKit); the display boundary in CGVirtualDisplayBridge.swift (virtual display creation).

MirageHostService.swift is the widest blast radius — it is split across nine + extension files (+AppStreaming, +DesktopStreaming, +Input, +VirtualDisplay, etc.), and StreamContext.swift at 1,299 lines carries the session state everything else touches. A change in either ripples across most of the host.

How To Use It

Add it as an SPM dependency, per the README:

.package(url: "https://github.com/EthanLipnik/MirageKit.git", from: "0.0.1"),

Requirements from the README: macOS 26+ for host streaming, iOS 26+ / visionOS 26+ for clients, Swift 6.2+. Host setup is try await hostService.start() with a MirageHostDelegate; client setup is try await clientService.connect(to: host) then requestWindowList(). No environment variables or config files are documented — configuration lives in Swift code (MirageStreamingSettings.swift, MirageAppPreferences.swift). The README notes this is active development with possible breaking changes.

Real-World Use

A Mac app that shares a specific window to an iPad: the host creates a MirageHostService, the client discovers it via Bonjour, requests the window list, and renders the decoded CVPixelBuffer in a MirageStreamView. Input events from the client's SwiftUI view route back through MirageHostInputController to the captured app.

Code Health & Issues

From static analysis (not opinion), 66 findings across 76 Swift files: 11 high, 50 medium, 5 low.

  • High — Deep nesting (25 occurrences): max indentation depth 8 in WindowCaptureEngine.swift, StreamContext.swift, MirageClientService.swift. Fix with early returns/guard clauses.
  • High — Duplicated code: 35 repeated 6-line blocks across 15 files, including StreamController.swift and MirageStreamContentView.swift. Extract shared helpers.
  • High — Oversized files (7): StreamContext.swift at 1,299 lines; MirageClientService.swift and MirageStreamView.swift also flagged. Split by responsibility.
  • Medium — High branching density (27): CursorMonitor.swift has 29 branch points over 66 lines. Consider strategy dispatch.
  • High — No CI: 76 source files, no workflow builds or tests anything. Add a build+test workflow on push and pull_request.
  • Medium — Tests: 1 test file against 76 source files (ratio 0.013). The suite cannot cover this codebase; add tests to the highest fan-in modules first.

The Bottom Line

The architecture is sound — clean separation of capture, encode, transport, and render, with a sensible public API. The risk is engineering discipline: no CI, nearly no tests, and several files too large to hold in one head. Use it if you need embeddable Apple-to-Apple P2P streaming and can absorb the maintenance cost of a pre-1.0 framework.