tvOS has no Local Network permission prompt
My tvOS app (AmbientCast, which watches a 3D
printer's camera) talks to hardware on the same Wi-Fi network: an RTSP
camera stream, an MQTT broker, and a multicast discovery probe to find the
printer in the first place. So the target declared
NSLocalNetworkUsageDescription, the way an iOS target would.
No prompt ever appeared. That did not look like a problem — plenty of
permissions get granted once and never asked again, and the networking
worked. What it did instead was supply an explanation for something else.
Connections at launch sometimes sat in NWConnection's
.waiting state for a few seconds, and two comments in my
networking code said why: the user had not answered the Local Network
prompt yet.
There is no prompt. There has never been one on this platform, and the key I had declared could not be displayed by anything on an Apple TV.
The answer is one row of one table
Apple's TN3179, Understanding local network privacy, lists the platforms that have the feature. Both absences are the interesting part:
Platform Supported Introduced
iOS yes iOS 14
iPadOS yes iPadOS 14
macOS yes macOS 15
tvOS no -
visionOS yes visionOS 1
watchOS no -
tvOS and watchOS do not have local network privacy. Not "it is granted automatically", not "it prompts somewhere else" — the mechanism is absent, so there is no privilege, no alert, and nothing in Settings. The same technote notes separately that "The simulator doesn’t support local network privacy." That one bites iOS developers; the table above is the tvOS version of the same surprise.
ELI5: what's a purpose string?
A sentence you put in your app's settings file explaining why you want access to something private — the camera, contacts, the local network. When the system asks the user for permission, your sentence is the text in the dialog. No dialog, nothing to read it.
The key's own documentation disagrees
Here is why this is easy to get wrong rather than careless. The reference
page for NSLocalNetworkUsageDescription
carries an availability badge reading tvOS 14.0+, and its
description is two sentences: "Any
app that uses the local network, directly or indirectly, should include
this description. This includes apps that use Bonjour and services
implemented with Bonjour, as well as direct unicast or multicast
connections to local hosts." My app makes unicast and multicast connections to
local hosts. I included the description.
NSBonjourServices
says tvOS 14.0+ too. So two plist keys advertise themselves as available on
a platform that the technote governing them says has nothing to configure.
The key reference is the page you land on from a search for the key name.
The technote is the page that is right.
The multicast side is at least consistent. TN3179 scopes it explicitly —
"Additionally,
if your iOS app performs multicast operations, sign it with the
com.apple.developer.networking.multicast
entitlement" — and adds that "The multicast entitlement isn’t
required on macOS." On tvOS you need neither it nor the purpose
string.
ELI5: what's multicast discovery?
Shouting into a room instead of calling someone by name. An app sends one message to a special address that every device on the network is listening to, and whatever it was looking for answers. It is how an app finds a printer or a speaker without being told its address first.
What the declared key cost
Nothing, directly. It is inert. The cost was that it read as evidence.
A purpose string in the bundle is a statement that the thing it configures
exists. So when a connection sat in .waiting at launch, "the
permission prompt hasn't been answered" was a tidy explanation standing
right next to a key that appeared to confirm it. That comment lived in my
code for nearly three months, in the one file someone would open to decide
whether the patience was still needed.
The direction that makes it dangerous is the correction, not the error.
.waiting at tvOS launch is real — it is Wi-Fi associating or
DNS settling after the device wakes, and the connection does reach
.ready on its own. Learning that tvOS has no prompt reads as
an argument for deleting the timeout that bounds the wait. That timeout is
the thing standing between an unreachable host and a connect call that
hangs
forever.
What actually happens on tvOS is simpler than the permission story. The app's discovery joins its multicast group and finds the printer about a second after launch. With the key removed, a 33-minute run on the Apple TV heard both printers announcing themselves every few seconds. There is nothing to grant and no state to check, which cuts both ways: you cannot be denied, and you cannot ask.
The same thing had happened with App Transport Security
The tvOS target also carried an NSAppTransportSecurity
dictionary with NSAllowsLocalNetworking in it, justified by
the printer's video and a diagnostic stream URL. Checking that turned out
to be the same exercise twice.
Neither NSAppTransportSecurity
nor NSAllowsLocalNetworking
lists tvOS in its availability at all — iOS, iPadOS, macOS and visionOS
only. And ATS is described as changing
"the
default security for HTTP connections". The camera and the broker are
not HTTP: both are raw NWConnection sockets rather than
URLSession requests.
ELI5: what's App Transport Security?
A rule Apple applies to an app's web requests: use HTTPS, with a modern enough encryption setup, or the request fails. Apps that genuinely need an exception declare it in the settings file. It governs web-style requests, not every byte an app puts on the network.
That was still reasoning, so I measured it. A release build with the dictionary removed, installed on a physical Apple TV 4K running tvOS 27, against a live camera: the app's local HLS server served 98 and 99 requests per minute against a 97-to-100 baseline, Picture in Picture became available 13 seconds after launch, and the log recorded no transport errors. The iOS target has never declared the key and drives the same loopback player. It was load-bearing for nothing.
What I'd tell you to check first
If you are porting something that talks to hardware on the LAN:
- On tvOS and watchOS, stop looking for the prompt. It does not exist. A connection stuck at launch is a path that isn't viable yet, a port already in use, or a host that isn't there.
- Trust the technote over the key reference. When a plist key's availability badge and the technote that governs the feature disagree, the badge is describing a key the OS will accept, not a mechanism it implements.
- Check which API you're actually using before reaching for ATS. It governs HTTP through the URL loading system. Raw sockets through Network.framework are a different path.
- Delete the key rather than keeping it as harmless. This is the part I would argue for hardest. An inert declaration costs nothing to execute and works as evidence forever.
- Keep the iOS declaration. iOS really does prompt, and that sentence is what your user reads. This is a per-platform answer, not a cleanup to apply everywhere.
The general version: a configuration key is not an observation. I had a purpose string, a plausible symptom and a documented API, and the three of them agreed with each other for nearly three months without any of them being checked against the platform. The thing that settled it was a table with a "no" in it, which no amount of reading my own code was going to produce.
Somebody asked this on the Apple Developer Forums in July 2021 — "Also there seems to be no 'local networking' permission dialog in tvOS - is that also correct?" — in a thread that is otherwise about a UDP port. An Apple engineer answered the multicast half and never that sentence. It is still unanswered, which is some of why this post exists: at the time, the technote carrying the table had not yet been published under its current name.