← All posts

Engineering

The display macOS won't let you ship

There is no public API to add a display to macOS. Every product that does it either uses a private one or sells a dongle — which decides how all of them are distributed.

6 minute read

I wanted a Mac to treat an iPad as a real second monitor. Not a mirror — a display macOS thinks is hardware, that you can arrange in System Settings, that windows move onto and stay on when you unplug.

macOS has no public API for that. Not a restricted one, not an entitlement you apply for. There is no supported way for a third-party application to add a display to macOS at all.

That single absence decides a surprising amount: what the feature costs to build, how fragile it is, and — the part I did not expect — how every product in this category is distributed.

What exists instead

The private API is CGVirtualDisplay, a set of CoreGraphics classes with no headers. You resolve them by name at runtime, build a descriptor, and get back something the window server treats as a monitor. Our wrapper says so at the top, because anyone reading it deserves to know what they are standing on:

/// A display macOS treats as real hardware, so the desktop *extends* onto it
/// instead of mirroring. There is no public API that adds a display, so this
/// sits on CoreDisplay's private classes, all resolved by name at runtime.

It works well. It is also load-bearing for the entire feature, undocumented, and free to move in any macOS release — which is the deal, and it is worth being explicit that it is a deal rather than an oversight on our part.

What people usually suggest instead does not substitute:

- Sidecar is Apple's own, with no third-party API. - AirPlay mirrors a display; it does not create one. - ScreenCaptureKit captures displays. It cannot make one. - DriverKit has no display family. The kernel extensions that used to do this are gone, and for the Mac App Store they were never viable anyway.

So the choice is the private API, or hardware.

Everyone else made the same choice

I assumed we had picked the awkward path and that the grown-up products had found something better. They have not. You can check this in about a minute, because Apple publishes a search endpoint that queries the Mac App Store specifically:

https://itunes.apple.com/search?term=duet%20display&entity=macSoftware

entity=macSoftware restricts results to the Mac App Store, so iOS listings cannot muddy the answer. Run it across the category and a clean split appears.

Not on the Mac App Store: Duet Display, Luna Display, BetterDisplay, Astropad. Also — and this is the part that generalises — Jump Desktop Connect and Screens Assist, the host-side halves of two remote-desktop products.

On the Mac App Store: Jump Desktop, Screens 5, bVNC — every one of them a *client*. Plus Apple Remote Desktop, which is Apple's, and Apple may use whatever it likes.

Every app in the store is a viewer. Every app that creates a display, captures a screen, or injects input is a direct download. There is no exception in the list.

A grid of three routes to adding a display on macOS. A private CoreGraphics API and a DriverKit hardware dongle each add a display but cannot ship on the App Store. Using only public API can ship on the App Store but adds no display. No route does both.ADDS A DISPLAYSHIPS ON THE APP STOREPrivate CoreGraphics APIDriverKit + a hardware donglePublic API onlyNO ROW HAS TWO TICKS — WHICH IS WHY NONE OF THESE APPS ARE IN THE STORE
The grid has no row with two ticks, and that is the whole distribution story for this category. Every product in it picked a row and accepted what the row costs.

Luna Display is the clearest case: it ships a physical dongle. That is not a gimmick or a licensing scheme, it is an architecture decision falling out of the same constraint. If you cannot ask macOS for a display, you can give it real hardware and let it discover one.

Why the store is closed to it

Two independent reasons, and fixing either leaves the other standing.

Private API. App Review guideline 2.5.1 prohibits it. Resolving classes by name at runtime does not change what the code does, and "their scanner might not notice" is not a licence — an app pulled after approval is worse than one never submitted.

The sandbox. Every Mac App Store app must be sandboxed. That is checked during processing, before a human sees it, and there is no version of this feature that survives it: a sandboxed process cannot create a virtual display, and CGEvent.post — which is how a viewer's tap becomes a real click — is silently blocked by the App Sandbox. Apple's own developer support has said plainly that general Accessibility APIs are not supported in sandboxed apps.

Sandbox the app and it compiles, ships, and quietly cannot do the two things it exists to do.

The shape everyone converges on

Once you accept the constraint, the same architecture appears everywhere, apparently independently:

The watching half goes in the store. The sharing half is a direct download.

Jump Desktop sells a client on the Mac App Store and gives away Jump Desktop Connect from its own site — and Connect asks for exactly the two permissions you would expect, Screen Recording and Accessibility. Screens does the same with Screens Assist. We arrived at it too: an iPhone and iPad viewer for the App Store, and the Mac host as a signed, notarised download.

That is not a workaround for being small. It is what the platform's rules produce when you follow them honestly, and three unrelated teams landing on the same shape is decent evidence it is the shape.

The bill for using it

Private API is not free, and the cost is not the one people warn you about. Everyone expects "it might break in a future macOS". What actually happened was subtler: a display's identity is a vendorID/productID/serialNum triple, we derived the serial from the process id, and macOS — correctly concluding it had never seen this monitor before — profiled each one and kept the profile forever. Seventy-two ICC profiles later, three ColorSync daemons were burning a full core on a machine where the app was not even running.

That is its own post, and the useful summary is: an undocumented API gives you no guidance about what the system will *remember* about how you used it. Ours registered something system-wide, and nothing in the absent documentation was going to mention that.

If you are deciding this for yourself

- Check the store before assuming a competitor solved it. The entity=macSoftware query takes a minute and is more reliable than anything written about the category, including by the vendors. - Being outside the Mac App Store is a normal way to ship a Mac app. Developer ID plus notarisation opens by double-click on a stranger's machine. The store is a distribution channel, not a permission slip. - Decide which half of your product is store-shaped. If a viewer, a client or a companion can be sandboxed without losing its point, that half can be in the store and can carry the discovery for the half that cannot. - Ask what the system persists. Before using any API that registers something system-wide, ask what outlives your process, and whether anything ever cleans it up. For a private API nobody will tell you, so the only way to know is to look at the disk before and after.

ExtendPilot shares a Mac's screen with the iPhones and iPads already in the room — as a mirror, or as a second desktop macOS treats as real hardware. Peer to peer over your own Wi-Fi, no account, no server in a session, so it works with the router unplugged. It is not out yet.

← All postsExtendPilot