General:
Forums subtopic: App & System Services > Networking
TN3151 Choosing the right networking API
Networking Overview document — Despite the fact that this is in the archive, this is still really useful.
TLS for App Developers forums post
Choosing a Network Debugging Tool documentation
WWDC 2019 Session 712 Advances in Networking, Part 1 — This explains the concept of constrained networking, which is Apple’s preferred solution to questions like How do I check whether I’m on Wi-Fi?
TN3135 Low-level networking on watchOS
TN3179 Understanding local network privacy
Adapt to changing network conditions tech talk
Understanding Also-Ran Connections forums post
Extra-ordinary Networking forums post
Foundation networking:
Forums tags: Foundation, CFNetwork
URL Loading System documentation — NSURLSession, or URLSession in Swift, is the recommended API for HTTP[S] on Apple platforms.
Moving to Fewer, Larger Transfers forums post
Testing Background Session Code forums post
Network framework:
Forums tag: Network
Network framework documentation — Network framework is the recommended API for TCP, UDP, and QUIC on Apple platforms.
Building a custom peer-to-peer protocol sample code (aka TicTacToe)
Implementing netcat with Network Framework sample code (aka nwcat)
Configuring a Wi-Fi accessory to join a network sample code
Moving from Multipeer Connectivity to Network Framework forums post
NWEndpoint History and Advice forums post
Wi-Fi (general):
How to modernize your captive network developer news post
Wi-Fi Fundamentals forums post
Filing a Wi-Fi Bug Report forums post
Working with a Wi-Fi Accessory forums post — This is part of the Extra-ordinary Networking series.
Wi-Fi (iOS):
TN3111 iOS Wi-Fi API overview technote
Wi-Fi Aware framework documentation
WirelessInsights framework documentation
iOS Network Signal Strength forums post
Network Extension Resources
Wi-Fi on macOS:
Forums tag: Core WLAN
Core WLAN framework documentation
Secure networking:
Forums tags: Security
Apple Platform Security support document
Preventing Insecure Network Connections documentation — This is all about App Transport Security (ATS).
WWDC 2017 Session 701 Your Apps and Evolving Network Security Standards [1] — This is generally interesting, but the section starting at 17:40 is, AFAIK, the best information from Apple about how certificate revocation works on modern systems.
Available trusted root certificates for Apple operating systems support article
Requirements for trusted certificates in iOS 13 and macOS 10.15 support article
About upcoming limits on trusted certificates support article
Apple’s Certificate Transparency policy support article
What’s new for enterprise in iOS 18 support article — This discusses new key usage requirements.
Technote 2232 HTTPS Server Trust Evaluation
Technote 2326 Creating Certificates for TLS Testing
QA1948 HTTPS and Test Servers
Miscellaneous:
More network-related forums tags: 5G, QUIC, Bonjour
On FTP forums post
Using the Multicast Networking Additional Capability forums post
Investigating Network Latency Problems forums post
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] This video is no longer available from Apple, but the URL should help you locate other sources of this info.
Networking
RSS for tagExplore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi,
We're hoping someone can help us determine why we're running into some odd behavior where a simple HTTP request is intermittently failing with error code NSURLErrorTimedOut (-1001)
Background:
HTTP request details:
The request is sent from a PacketTunnelProvider and is meant to be a Captive Portal check.
The request is insecure (HTTP, instead of HTTPS) but we have configured App Transport Security (ATS) to allow insecure HTTP loads from this hostname.
See info.plist excerpt below.
The request is sent using NSMutableURLRequest/NSURLSessionDataTask using an Ephemeral session configuration.
We only modify 2 properties on NSMutableURLRequest
The timeoutInterval property is set to 5 seconds.
The allowsCellularAccess property is set to NO.
No headers or other configuration are modified.
NSURLSessionDataTask completionHandler receives an NSError:
We checked the NSError's userInfo dictionary for an underlying error (NSUnderlyingErrorKey).
The underlying error shows the same code NSURLErrorTimedOut (-1001).
We haven't seen any underlying errors with code NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022) .
On a laptop, we confirmed that the Captive portal check site is accessible and loads correctly.
Laptop and iOS device are on the same Wi-fi.
I've witnessed the error in the debugger, and been able to load the site on my laptop at the same time.
So, we don't have any reason to believe this is server related.
The PacketTunnelProvider is configured to only handle DNS queries and is not intercepting/routing the HTTP traffic.
The DNS query for the Captive portal request is handled correctly.
In fact, outside of the PacketTunnelProvider, all sites load in Mobile Safari.
So, we're not breaking internet on this device.
In other words, we have no reason to believe our DNS handling is interfering with the HTTP request since other HTTP requests are working as expected.
We setup CFNetwork Diagnostic Logging (https://developer.apple.com/documentation/network/debugging-https-problems-with-cfnetwork-diagnostic-logging)
In console.app, we are able to find some logging on the Timeout
See excerpt from Console.app's log below.
We confirmed that the nscurl tool did not flag the request (https://developer.apple.com/documentation/security/identifying-the-source-of-blocked-connections)
All ATS tests run with nscurl were successful.
See nscurl command used below.
Questions:
What are next steps to debug this intermittent timeout?
What should we look for in the CFNetwork Diagnostic Logging to help debug the issue further?
Thanks in advance for your help!
ATS configuration setup in both the UI and the PacketTunnel's info.plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>subdomain.subdomain.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Excerpt from Console.app's log:
CFNetwork Example PacketTunnel 10836 Diagnostics default 11:30:33.029032-0700 CFNetwork Diagnostics [3:834] 11:30:32.946 {
Did Timeout: (null)
Loader: request GET http://subdomain.subdomain.example.com/content/cpcheck.txt HTTP/1.1
Timeout Interval: 5.000 seconds
init to origin load: 0.000592947s
total time: 5.00607s
total bytes: 0
} [3:834]
nscurl command
$ /usr/bin/nscurl --ats-diagnostics --verbose http://subdomain.subdomain.example.com/content/cpcheck.txt
IMPORTANT The resume rate limiter is now covered by the official documentation. See Use background sessions efficiently within Downloading files in the background. So, the following is here purely for historical perspective.
NSURLSession’s background session support on iOS includes a resume rate limiter. This limiter exists to prevent apps from abusing the background session support in order to run continuously in the background. It works as follows:
nsurlsessiond (the daemon that does all the background session work) maintains a delay value for your app.
It doubles that delay every time it resumes (or relaunches) your app.
It resets that delay to 0 when the user brings your app to the front.
It also resets the delay to 0 if the delay period elapses without it having resumed your app.
When your app creates a new task while it is in the background, the task does not start until that delay has expired.
To understand the impact of this, consider what happens when you download 10 resources. If you pass them to the background session all at once, you see something like this:
Your app creates tasks 1 through 10 in the background session.
nsurlsessiond starts working on the first few tasks.
As tasks complete, nsurlsessiond starts working on subsequent ones.
Eventually all the tasks complete and nsurlsessiond resumes your app.
Now consider what happens if you only schedule one task at a time:
Your app creates task 1.
nsurlsessiond starts working on it.
When it completes, nsurlsessiond resumes your app.
Your app creates task 2.
nsurlsessiond delays the start of task 2 a little bit.
nsurlsessiond starts working on task 2.
When it completes, nsurlsessiond resumes your app.
Your app creates task 3.
nsurlsessiond delays the start of task 3 by double the previous amount.
nsurlsessiond starts working on task 3.
When it completes, nsurlsessiond resumes your app.
Steps 8 through 11 repeat, and each time the delay doubles. Eventually the delay gets so large that it looks like your app has stopped making progress.
If you have a lot of tasks to run then you can mitigate this problem by starting tasks in batches. That is, rather than start just one task in step 1, you would start 100. This only helps up to a point. If you have thousands of tasks to run, you will eventually start seeing serious delays. In that case it’s much better to change your design to use fewer, larger transfers.
Note All of the above applies to iOS 8 and later. Things worked differently in iOS 7. There’s a post on DevForums that explains the older approach.
Finally, keep in mind that there may be other reasons for your task not starting. Specifically, if the task is flagged as discretionary (because you set the discretionary flag when creating the task’s session or because the task was started while your app was in the background), the task may be delayed for other reasons (low power, lack of Wi-Fi, and so on).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
(r. 22323366)
It works when one device is only a publisher and the other is only a subscriber. However, when both devices act as both publisher and subscriber simultaneously—which Apple’s documentation (https://developer.apple.com/documentation/wifiaware/adopting-wi-fi-aware#Declare-services) indicates is valid—the connection never establishes. After timing out, both NetworkListener and NetworkBrowser transition to the failed state. This appears to be a race condition in Network framework.
Task.detached {
try await NetworkListener(
for: .wifiAware(
.connecting(
to: .myService,
from: .allPairedDevices,
datapath: .defaults
)
),
using: .parameters {
Coder(
sending: ...,
receiving: ...,
using: NetworkJSONCoder()
) {
TCP()
}
}
).run { connection in
await self.add(connection: connection)
}
}
Task.detached {
try await NetworkBrowser(
for: .wifiAware(
.connecting(
to: .allPairedDevices,
from: .myService
)
),
using: .tcp
).run { endpoints in
for endpoint in endpoints {
await self.connect(to: endpoint)
}
}
}
Hi!
I'm working on a solution (iOS 18) that uses Network Extensions PacketTunnelProvider and Content Filter. Currently I'm trying to integrate it with another extension – DNSProxyProvider. My goal is to process dns queries and use resolved ips and names for additional routing inside of the packet tunnel. I'm running into a major issue: whenever both VPN and DNS proxy are active simultaneously, the device completely loses internet connectivity — no traffic goes through, and DNS resolution seems to stop working entirely.
I know about the mdm supervision requirement to use DNSProxyProvider and that's covered as I work with a managed device and install a DNS proxy profile, here's how its .mobileconfig file looks like:
The DNS proxy itself works fine when working by itself (without VPN being turned on), as I implemented it that it successfully processes DNS packets flows while collecting information about domains etc, and everything works perfectly. Problems begin when using VPN at the same time. I'm aware that tunnel settings include dns related options that can affect this, but I haven't had much luck with tweaking them. Here's how they look right now for reference:
let settings: NEPacketTunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "240.0.0.1")
// let dnsSettings = NEDNSSettings(servers: "8.8.8.8,8.8.4.4".components(separatedBy: ","))
// dnsSettings.matchDomains = [""]
// settings.dnsSettings = dnsSettings
settings.proxySettings = nil
/* ipv4 settings */
let ipv4Settings = NEIPv4Settings(addresses: ["240.0.0.2"], subnetMasks: ["255.255.255.0"])
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
settings.ipv4Settings = ipv4Settings
/* MTU */
settings.mtu = 1500
return settings
I've tried excluding some dns related ip routes and dns settings shenanigans but nothing.
I haven't found any information that might suggest that using both of these extensions at the same time doesn't work, on the contrary, this page in the official documentation about the expected use of packet tunnel provider the expected use of packet tunnel provider, as it talks about the fact that you should not use it for interception of all of DNS traffic, as the use of DNSPRoxyProvider (or dns settings) are built for that, which in my mind, suggests that there should be no problem with using them both and just splitting the dns traffic handling to the proxy.
Will be thankful for any help!
I need to run multiple, slightly different copies of a modeling tool, which all need access to a model repository on a different machine. Security Settings -> Network tends to pick one modeling tool (and unfortunately the wrong one) for permission, but the dialog offers no way to add the other copies manually. Where can I configure the permission on low level.
[macOS Sequoia 15.6.1]
Topic:
App & System Services
SubTopic:
Networking
Description
Our NETransparentProxyProvider system extension maintains a persistent TLS/DTLS control channel to a security gateway. To maintain this stateful connection the extension sends application-level "Keep Alive" packets every few seconds (example : 20 seconds).
The Issue: When the macOS device enters a sleep state, the Network Extension process is suspended, causing our application-level heartbeat to cease. Consequently, our backend gateway—detecting no activity—terminates the session via Dead Peer Detection (DPD).
The problem is exacerbated by macOS Dark Wake cycles. We observe the extension's wake() callback being triggered periodically (approx. every 15 minutes) while the device remains in a sleep state (lid closed). During these brief windows:
The extension attempts to use the existing socket, finds it terminated by the backend, and initiates a full re-handshake.
Shortly after the connection is re-established, the OS triggers the sleep() callback and suspends the process again.
This creates a "connection churn" cycle that generates excessive telemetry noise and misleading "Session Disconnected" alerts for our enterprise customers.
Steps to Reproduce
Activate Proxy:
Start the NETransparentProxyProvider and establish a TLS session to a gateway.
Apply Settings: Configure NETransparentProxyNetworkSettings to intercept outbound TCP/UDP traffic.
Initialize Heartbeat: Start a 20-second timer (DispatchSourceTimer) to log and send keep-alive packets.
Induce Sleep: Put the Mac to sleep (Apple Menu > Sleep).
Observe Logs: Monitor the system via sysdiagnose or the macOS Console.
Observation: Logs stop entirely during sleep, indicating process suspension.
Observation: wake() and sleep() callbacks are triggered repeatedly during Dark Wake intervals, causing a cycle of re-connections.
Expected Behavior
We seek to minimize connection turnover during maintenance wakes and maintain session stability while the device is technically in a sleep state.
Questions for Apple
Is it possible to suppress the sleep and wake callback methods of NETransparentProxyProvider when the device is performing a maintenance/Dark Wake, only triggering them for a full user-initiated wake?
Is it possible to prevent the NETransparentProxyProvider process from being suspended during sleep, or at least grant it a high-priority background execution slot to maintain the heartbeat?
If suspension is mandatory, is there a recommended way to utilize TCP_KEEPALIVE socket options that the kernel can handle on behalf of the suspended extension?
How can the extension programmatically identify if a wake() call is a "Dark Wake" versus a "Full User Wake" to avoid unnecessary re-connection logic?
All of our uses of CFSockets have started causing crashes in iOS 16. They seem to be deprecated so we are trying to transition over to using the Network framework and NWConnection to try to fix the crashes.
One of our uses of them is to ping a device on the local network to make sure it is there and online and provide a heartbeat status in logs as well as put the application into a disabled state if it is not available as it is critical to the functionality of the app. I know it is discouraged to disable any functionality based on the reachability of a resource but this is in an enterprise environment where the reachability of this device is mission critical.
I've seen other people ask about the ability to ping with the Network framework and the answers I've found have said that this is not possible and pointed people to the SimplePing sample code but it turns out our existing ping code is already using this technique and it is crashing just like our other CFSocket usages, inside CFSocketInvalidate with the error BUG IN CLIENT OF LIBPLATFORM: Trying to recursively lock an os_unfair_lock.
Is there any updated way to perform a ping without using the CFSocket APIs that now seem to be broken/unsupported on iOS 16?
Hey there
Are there any recommendations or guidance for apps on alternatives to certificate pinning to secure their device network traffic?
I want to move away from the overhead and risk associated with rotating certificates when using leaf pinning.
However, I also don't want people to be able to perform a MITM attack easily using something like Charles Proxy with a self‑signed certificate added to the trust store.
My understanding is that an app cannot distinguish between user‑trusted certificates and system‑trusted certificates in the trust store, so it cannot block traffic that uses user‑trusted certificates.
Topic:
App & System Services
SubTopic:
Networking
Hello,
I’ve run into some strange behavior with the macOS System Extension using a Packet Tunnel. The issue showed up after the device went to sleep while the VPN was running. When I woke the computer, the VPN tried to reconnect but never succeeded — it just stayed stuck in the “connecting” state.
I was able to turn the VPN off, but every attempt to turn it back on failed and got stuck at “connecting” again. Even removing the VPN configuration from Settings didn’t help. The only thing that worked was disabling the system extension completely.
While checking the logs, I noticed thousands of identical log messages appearing within just a few seconds:
nesessionmanager(562) deny(1) system-fsctl (_IO "h" 47)
17:11:52.481498+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5454 com.apple.networkextension
17:11:52.481568+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5454 com.apple.networkextension
17:11:52.481580+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5454 com.apple.networkextension
17:11:52.481587+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5454 com.apple.networkextension
17:11:52.481646+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
17:11:52.481664+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
17:11:52.481671+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
17:11:52.481676+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
17:11:52.481682+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
17:11:52.481687+0200 NESMVPNSession[Primary Tunnel:Secure DNS: got On Demand start message from pid 5446 com.apple.networkextension
After the burst of these repeated messages, I started seeing logs like the following:
17:11:52.481759+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.481790+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.481949+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.481966+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.481986+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.481992+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.482003+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.482011+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.482022+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.482028+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.482039+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Spotify Helper[69038] com.apple.networkextension
17:11:52.482049+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Spotify Helper[69038]: session in state connecting com.apple.networkextension
17:11:52.482060+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from Slack Helper[84828] com.apple.networkextension
17:11:52.482069+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from Slack Helper[84828]: session in state connecting com.apple.networkextension
17:11:52.482079+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Received a start command from sharingd[764] com.apple.networkextension
17:11:52.482086+0200 NESMVPNSession[Primary Tunnel:Secure DNS: Skip a start command from sharingd[764]: session in state connecting com.apple.networkextension
It is clear that the connection is in a loop of submitting request to start and then failing. This problem occured only after sleep on macOS 26.0 and 15.6.
This issue only occured after the system woke up from sleep. macOS 15.6 and 26.0.
Is this a known problem, and how should I go about troubleshooting or resolving it?
I’m developing a iOS VPN app, and I need to execute a task in the main app even when it’s in the background or killed state. I know the Network Extension continues running during those times. Is there a way for the extension to immediately notify the app or trigger a task on the app side?
I'm trying to use ThreadNetwork API to manage TheradNetworks on device (following this documentation: https://developer.apple.com/documentation/threadnetwork/), but while some functions on THClient work (such as getPreferedNetwork), most don't (storeCredentials, retrieveAllCredentials). When calling these functions I get the following warning/error:
Client: -[THClient getConnectionEntitlementValidity]_block_invoke - Error:
-[THClient storeCredentialsForBorderAgent:activeOperationalDataSet:completion:]_block_invoke:701: - Error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process.}
Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process." UserInfo={NSDebugDescription=The connection to service with pid 414 named com.apple.ThreadNetwork.xpc was invalidated from this process.}
Failed to store Thread credentials: Couldn’t communicate with a helper application.
STEPS TO REPRODUCE
Create new project
Add Thread Network capability via Xcode UI (com.apple.developer.networking.manage-thread-network-credentials)
Trigger storeCredentials
let extendedMacData = "9483C451DC3E".hexadecimal
let tlvHex = "0e080000000000010000000300001035060004001fffe002083c66f0dc9ef53f1c0708fdb360c72874da9905104094dce45388fd3d3426e992cbf0697b030d474c2d5332302d6e65773030310102250b04106c9f919a4da9b213764fc83f849381080c0402a0f7f8".hexadecimal
// Initialize the THClient
let thClient = THClient()
// Store the credentials
await thClient.storeCredentials(forBorderAgent: extendedMacData!, activeOperationalDataSet: tlvHex!) { error in
if let error = error {
print(error)
print("Failed to store Thread credentials: \(error.localizedDescription)")
} else {
print("Successfully stored Thread credentials")
}
}
NOTES:
I tried with first calling getPreferedNetwork to initiate network permission dialog
Tried adding meshcop to bojur services
Tried with different release and debug build configurations
For important background information, read Extra-ordinary Networking before reading this.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Broadcasts and Multicasts, Hints and Tips
I regularly see folks struggle with broadcasts and multicasts on Apple platforms. This post is my attempt to clear up some of the confusion.
This post covers both IPv4 and IPv6. There is, however, a key difference. In IPv4, broadcasts and multicasts are distinct concepts. In contrast, IPv6 doesn’t support broadcast as such; rather, it treats broadcasts as a special case of multicasts. IPv6 does have an all nodes multicast address, but it’s rarely used.
Before reading this post, I suggest you familiarise yourself with IP addresses in general. A good place to start is The Fount of All Knowledge™.
Service Discovery
A lot of broadcast and multicast questions come from folks implementing their own service discovery protocol. I generally recommend against doing that, for the reasons outlined in the Service Discovery section of Don’t Try to Get the Device’s IP Address.
There are, however, some good reasons to implement a custom service discovery protocol. For example, you might be working with an accessory that only supports this custom protocol [1]. If you must implement your own service discovery protocol, read this post and also read the advice in Don’t Try to Get the Device’s IP Address.
IMPORTANT Sometimes I see folks implementing their own version of mDNS. This is almost always a mistake:
If you’re using third-party tooling that includes its own mDNS implementation, it’s likely that this tooling allows you to disable that implementation and instead rely on the Bonjour support that’s built-in to all Apple platforms.
If you’re doing some weird low-level thing with mDNS or DNS-SD, it’s likely that you can do that with the low-level DNS-SD API.
[1] And whose firmware you can’t change! I talk more about this in Working with a Wi-Fi Accessory.
API Choice
Broadcasts and multicasts typically use UDP [1]. TN3151 Choosing the right networking API describes two recommended UDP APIs:
Network framework
BSD Sockets
Our general advice is to prefer Network framework over BSD Sockets, but UDP broadcasts and multicasts are an exception to that rule. Network framework has very limited UDP broadcast support. And while it’s support for UDP multicasts is less limited, it’s still not sufficient for all UDP applications. In cases where Network framework is not sufficient, BSD Sockets is your only option.
[1] It is possible to broadcast and multicast at the Ethernet level, but I almost never see questions about that.
UDP Broadcasts in Network Framework
Historically I’ve claimed that Network framework was useful for UDP broadcasts is very limited circumstances (for example, in the footnote on this post). I’ve since learnt that this isn’t the case. Or, more accurately, this support is so limited (r. 122924701) as to be useless in practice.
For the moment, if you want to work with UDP broadcasts, your only option is BSD Sockets.
UDP Multicasts in Network Framework
Network framework supports UDP multicast using the NWConnectionGroup class with the NWMulticastGroup group descriptor. This support has limits. The most significant limit is that it doesn’t support broadcasts; it’s for multicasts only.
Note This only relevant to IPv4. Remember that IPv6 doesn’t support broadcasts as a separate concept.
There are other limitations, but I don’t have a good feel for them. I’ll update this post as I encounter issues.
Local Network Privacy
Some Apple platforms support local network privacy. This impacts broadcasts and multicasts in two ways:
Broadcasts and multicasts require local network access, something that’s typically granted by the user.
Broadcasts and multicasts are limited by a managed entitlement (except on macOS).
TN3179 Understanding local network privacy has lots of additional info on this topic, including the list of platforms to which it applies.
Send, Receive, and Interfaces
When you broadcast or multicast, there’s a fundamental asymmetry between send and receive:
You can reasonable receive datagrams on all broadcast-capable interfaces.
But when you send a datagram, it has to target a specific interface.
The sending behaviour is the source of many weird problems. Consider the IPv4 case. If you send a directed broadcast, you can reasonably assume it’ll be routed to the correct interface based on the network prefix. But folks commonly send an all-hosts broadcast (255.255.255.255), and it’s not obvious what happens in that case.
Note If you’re unfamiliar with the terms directed broadcast and all-hosts broadcast, see IP address.
The exact rules for this are complex, vary by platform, and can change over time. For that reason, it’s best to write your broadcast code to be interface specific. That is:
Identify the interfaces on which you want to work.
Create a socket per interface.
Bind that socket to that interface.
Note Use the IP_BOUND_IF (IPv4) or IPV6_BOUND_IF (IPv6) socket options rather than binding to the interface address, because the interface address can change over time.
Extra-ordinary Networking has links to other posts which discuss these concepts and the specific APIs in more detail.
Miscellaneous Gotchas
A common cause of mysterious broadcast and multicast problems is folks who hard code BSD interface names, like en0. Doing that might work for the vast majority of users but then fail in some obscure scenarios.
BSD interface names are not considered API and you must not hard code them. Extra-ordinary Networking has links to posts that describe how to enumerate the interface list and identify interfaces of a specific type.
Don’t assume that there’ll be only one interface of a given type. This might seem obviously true, but it’s not. For example, our platforms support peer-to-peer Wi-Fi, so each device has multiple Wi-Fi interfaces.
When sending a broadcast, don’t forget to enable the SO_BROADCAST socket option.
If you’re building a sandboxed app on the Mac, working with UDP requires both the com.apple.security.network.client and com.apple.security.network.server entitlements.
Some folks reach for broadcasts or multicasts because they’re sending the same content to multiple devices and they believe that it’ll be faster than unicasts. That’s not true in many cases, especially on Wi-Fi. For more on this, see the Broadcasts section of Wi-Fi Fundamentals.
Snippets
To send a UDP broadcast:
func broadcast(message: Data, to interfaceName: String) throws {
let fd = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)
defer { try! fd.close() }
try fd.setSocketOption(SOL_SOCKET, SO_BROADCAST, 1 as CInt)
let interfaceIndex = if_nametoindex(interfaceName)
guard interfaceIndex > 0 else { throw … }
try fd.setSocketOption(IPPROTO_IP, IP_BOUND_IF, interfaceIndex)
try fd.send(data: message, to: ("255.255.255.255", 2222))
}
Note These snippet uses the helpers from Calling BSD Sockets from Swift.
To receive UDP broadcasts:
func receiveBroadcasts(from interfaceName: String) throws {
let fd = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)
defer { try! fd.close() }
let interfaceIndex = if_nametoindex(interfaceName)
guard interfaceIndex > 0 else { fatalError() }
try fd.setSocketOption(IPPROTO_IP, IP_BOUND_IF, interfaceIndex)
try fd.setSocketOption(SOL_SOCKET, SO_REUSEADDR, 1 as CInt)
try fd.setSocketOption(SOL_SOCKET, SO_REUSEPORT, 1 as CInt)
try fd.bind("0.0.0.0", 2222)
while true {
let (data, (sender, port)) = try fd.receiveFrom()
…
}
}
IMPORTANT This code runs synchronously, which is less than ideal. In a real app you’d run the receive asynchronously, for example, using a Dispatch read source. For an example of how to do that, see this post.
If you need similar snippets for multicast, lemme know. I’ve got them lurking on my hard disk somewhere (-:
Other Resources
Apple’s official documentation for BSD Sockets is in the man pages. See Reading UNIX Manual Pages. Of particular interest are:
setsockopt man page
ip man page
ip6 man page
If you’re not familiar with BSD Sockets, I strongly recommend that you consult third-party documentation for it. BSD Sockets is one of those APIs that looks simple but, in reality, is ridiculously complicated. That’s especially true if you’re trying to write code that works on BSD-based platforms, like all of Apple’s platforms, and non-BSD-based platforms, like Linux.
I specifically recommend UNIX Network Programming, by Stevens et al, but there are lots of good alternatives.
https://unpbook.com
Revision History
2025-09-01 Fixed a broken link.
2025-01-16 First posted.
Before iOS16, we can use
https://developer.apple.com/documentation/coretelephony/ctcarrier
But after iOS this is deprecated and has no replacement.
There are some discussions on it, eg.
https://developer.apple.com/forums/thread/714876
https://developer.apple.com/forums/thread/770400
Now I asked AI, then it provided this solution, to check the serviceCurrentRadioAccessTechnology, so it this ok to check the SIM card status?
var hasSIMCard = false
let info = CTTelephonyNetworkInfo()
if let rat = info.serviceCurrentRadioAccessTechnology,
rat.values.contains(where: { !$0.isEmpty }) {
hasSIMCard = true. // has RAT
}
BTW, I can see a lot of changes in the Core Telephony framework.
https://developer.apple.com/documentation/coretelephony
1.isSIMInserted
https://developer.apple.com/documentation/coretelephony/ctsubscriber/issiminserted
A Boolean property that indicates whether a SIM is present. iOS 18.0+ iPadOS 18.0+
This value property is true if the system finds a SIM matching the Info.plist carrier information (MCC / MNC / GID1 / GID2).
Is this ok to check SIM insert status, this seems must preconfig some info in the info.plist.
2.iOS26 provide CTCellularPlanStatus
https://developer.apple.com/documentation/coretelephony/ctcellularplanstatus
Can I use this to check SIM status?
ios構成プロファイルの制限のallowCloudPrivateRelayのプライベートリレーの制御とRelayペイロードの機能は関係がありますか?
それとも別々の機能でしょうか?
↓
s there a relationship between the private relay control in the iOS configuration profile restriction allowCloudPrivateRelay and the functionality of the Relay payload?
Or are they separate features?
Topic:
App & System Services
SubTopic:
Networking
Is this even possible? Instead of any pairing dialog appearing, my central code get the "Authentication is insufficient" error when reading the characteristic.
My peripheral (in the macOS app) code uses the .notifyEncryptionRequired property and uses .readEncryptionRequired and .writeEncryptionRequired permissions. No descriptors are set, but I think they get added automatically since this characteristic notifies. 2900 and 2902 descriptors are set by the peripheral/CoreBluetooth.
If the Mac and iPhone are using the same Apple ID does that affect pairing?
Apology for repost. I needed to fix the tags for original thread.
https://developer.apple.com/forums/thread/777159
On iOS 18.3, I noted that partition "HTTPCookiePropertyKey: StoragePartition" is not observed to be set for cookies returned from the wkwebview cookie store.
Now on 18.4 beta 4 we are now seeing those same cookies are populated with a partition property. Is there documentation for this change? Is it intended to be suddenly populated in 18.4?
Now that partition property is set, HTTPCookieStorage.shared.cookies(for: serverUri) doesn't seem to return the expected cookies correctly. For context, we are using the cookies extracted from wkwebview, setting them in HTTPCookieStorage.shared and using URLSession to make network calls outside the webivew. Works fine once I forcefully set partition on the cookie to nil.
More details on what the cookie looks like here: https://feedbackassistant.apple.com/feedback/16906526
Hopefully this is on your radar?
Hey!
We discovered an unexpected side-effect of enabling enforceRoutes in our iOS VPN application - video airplay from iOS to tvOS stopped working (Unable to Connect popup appears instead).
Our flags combination is:
includeAllNetworks = false
enforceRoutes = true
excludeLocalNetworks = true
Interestingly, music content can be AirPlayed with the same conditions.
Also, video AirPlay from iOS device to the macOS works flawlessly.
Do you know if this is a known issue? Do you have any advice if we can fix this problem on our side, while keeping enforcRoutes flag enabled?
At WWDC 2015 Apple announced two major enhancements to the Network Extension framework:
Network Extension providers — These are app extensions that let you insert your code at various points within the networking stack, including:
Packet tunnels via NEPacketTunnelProvider
App proxies via NEAppProxyProvider
Content filters via NEFilterDataProvider and NEFilterControlProvider
Hotspot Helper (NEHotspotHelper) — This allows you to create an app that assists the user in navigating a hotspot (a Wi-Fi network where the user must interact with the network in order to get access to the wider Internet).
Originally, using any of these facilities required authorisation from Apple. Specifically, you had to apply for, and be granted access to, a managed capability. In Nov 2016 this policy changed for Network Extension providers. Any developer can now use the Network Extension provider capability like they would any other capability.
There is one exception to this rule: Network Extension app push providers, introduced by iOS 14 in 2020, still requires that Apple authorise the use of a managed capability. To apply for that, follow the link in Local push connectivity.
Also, the situation with Hotspot Helpers remains the same: Using a Hotspot Helper, requires that Apple authorise that use via a managed capability. To apply for that, follow the link in Hotspot helper.
IMPORTANT Pay attention to this quote from the documentation:
NEHotspotHelper is only useful for hotspot integration. There are
both technical and business restrictions that prevent it from being
used for other tasks, such as accessory integration or Wi-Fi based
location.
The rest of this document answers some frequently asked questions about the Nov 2016 change.
#1 — Has there been any change to the OS itself?
No, this change only affects the process by which you get the capabilities you need in order to use existing Network Extension framework facilities. Previously these were managed capabilities, meaning their use was authorised by Apple. Now, except for app push providers and Hotspot Helper, you can enable the necessary capabilities using Xcode’s Signing & Capabilities editor or the Developer website.
IMPORTANT Some Network Extension providers have other restrictions on their use. For example, a content filter can only be used on a supervised device. These restrictions are unchanged. See TN3134 Network Extension provider deployment for the details.
#2 — How exactly do I enable the Network Extension provider capability?
In the Signing & Capabilities editor, add the Network Extensions capability and then check the box that matches the provider you’re creating.
In the Certificates, Identifiers & Profiles section of the Developer website, when you add or edit an App ID, you’ll see a new capability listed, Network Extensions. Enable that capability in your App ID and then regenerate the provisioning profiles based on that App ID.
A newly generated profile will include the com.apple.developer.networking.networkextension entitlement in its allowlist; this is an array with an entry for each of the supported Network Extension providers. To confirm that this is present, dump the profile as shown below.
$ security cms -D -i NETest.mobileprovision
…
<plist version="1.0">
<dict>
…
<key>Entitlements</key>
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
<string>content-filter-provider</string>
<string>app-proxy-provider</string>
… and so on …
</array>
…
</dict>
…
</dict>
</plist>
#3 — I normally use Xcode’s Signing & Capabilities editor to manage my entitlements. Do I have to use the Developer website for this?
No. Xcode 11 and later support this capability in the Signing & Capabilities tab of the target editor (r. 28568128 ).
#4 — Can I still use Xcode’s “Automatically manage signing” option?
Yes. Once you modify your App ID to add the Network Extension provider capability, Xcode’s automatic code signing support will include the entitlement in the allowlist of any profiles that it generates based on that App ID.
#5 — What should I do if I previously applied for the Network Extension provider managed capability and I’m still waiting for a reply?
Consider your current application cancelled, and use the new process described above.
#6 — What should I do if I previously applied for the Hotspot Helper managed capability and I’m still waiting for a reply?
Apple will continue to process Hotspot Helper managed capability requests and respond to you in due course.
#7 — What if I previously applied for both Network Extension provider and Hotspot Helper managed capabilities?
Apple will ignore your request for the Network Extension provider managed capability and process it as if you’d only asked for the Hotspot Helper managed capability.
#8 — On the Mac, can Developer ID apps host Network Extension providers?
Yes, but there are some caveats:
This only works on macOS 10.15 or later.
Your Network Extension provider must be packaged as a system extension, not an app extension.
You must use the *-systemextension values for the Network Extension entitlement (com.apple.developer.networking.networkextension).
For more on this, see Exporting a Developer ID Network Extension.
#9 — After moving to the new process, my app no longer has access to the com.apple.managed.vpn.shared keychain access group. How can I regain that access?
Access to this keychain access group requires another managed capability. If you need that, please open a DTS code-level support request and we’ll take things from there.
IMPORTANT This capability is only necessary if your VPN supports configuration via a configuration profile and needs to access credentials from that profile (as discussed in the Profile Configuration section of the NETunnelProviderManager Reference). Many VPN apps don’t need this facility.
If you were previously granted the Network Extension managed capability (via the process in place before Nov 2016), make sure you mention that; restoring your access to the com.apple.managed.vpn.shared keychain access group should be straightforward in that case.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Revision History
2025-11-11 Removed the discussion of TSI assets because those are no longer a thing.
2025-09-12 Adopted the code-level support request terminology. Made other minor editorial changes.
2023-01-11 Added a discussion of Network Extension app push providers. Added a link to Exporting a Developer ID Network Extension. Added a link to TN3134. Made significant editorial changes.
2020-02-27 Fixed the formatting. Updated FAQ#3. Made minor editorial changes.
2020-02-16 Updated FAQ#8 to account for recent changes. Updated FAQ#3 to account for recent Xcode changes. Made other editorial changes.
2016-01-25 Added FAQ#9.
2016-01-6 Added FAQ#8.
2016-11-11 Added FAQ#5, FAQ#6 and FAQ#7.
2016-11-11 First posted.
Greetings,
According to Apple's Wi-Fi Aware documentation (https://developer.apple.com/documentation/wifiaware) the Wi-Fi Aware APIs can be used only with peer devices that have been paired. Pairing can be performed using AccessorySetupKit or DeviceDiscoveryUI.
Unfortunately, the sample code for Wi-Fi Aware doesn't include either of these APIs. (https://developer.apple.com/documentation/wifiaware/building-peer-to-peer-apps)
Looking at the sample code for AccessorySetupKit (https://developer.apple.com/documentation/accessorysetupkit/setting-up-and-authorizing-a-bluetooth-accessory) there is only an example using Bluetooth. And the AccessorySetupKit APIs don't yet document how Wi-Fi Aware is used or how one sets up the Info.plist with the appropriate keys.
Can Apple update its example code to fill in these gaps or point me to documentation that can fill in these gaps? It is hard to develop an understanding of the capabilities of these APIs when they are so poorly documented.
Thanks for any help,
Smith
Hi, I am making a AI-Powered app that makes api requests to the openai API. However, for security, I set up a vercel backend that handles the API calls securely, while my frontend makes a call to my vercel-hosted https endpoint. Interestingly, whenever I try to make that call on my device, an iPhone, I get this error:
Task <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10> finished with error [-1003] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={_kCFStreamErrorCodeKey=-72000, NSUnderlyingError=0x1435783f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorDomainKey=10, _kCFStreamErrorCodeKey=-72000, _NSURLErrorNWResolutionReportKey=Resolved 0 endpoints in 3ms using unknown from query, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: pdp_ip0[lte], ipv4, ipv6, dns, expensive, uses cell}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>"
), NSLocalizedDescription=A server with the specified hostname could not be found., NSErrorFailingURLStringKey=https://[my endpoint], NSErrorFailingURLKey=https://[my endpoint], _kCFStreamErrorDomainKey=10}
I'm completely stuck because when I directly make https requests to other api's like openai's endpoint, without the proxy, it finds the server completely fine. Running my endpoint on terminal with curl also works as intended, as I see api key usages. But for some reason, on my project, it does not work. I've looked through almost every single post I could find online, but a lot all of the solutions are outdated and unhelpful.
I'm willing to schedule a call, meeting, whatever to resolve this issue and get help more in depth as well.