Networking

RSS for tag

Explore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.

Networking Documentation

Posts under Networking subtopic

Post

Replies

Boosts

Views

Activity

Networking Resources
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.
0
0
4.1k
2w
Debugging a Network Extension Provider
I regularly see folks struggle to debug their Network Extension providers. For an app, and indeed various app extensions, debugging is as simple as choosing Product > Run in Xcode. That’s not the case with a Network Extension provider, so I thought I’d collect together some hints and tips to help you get started. If you have any comments or questions, create a new thread here on DevForums. Put it in the App & System Services > Networking and tag it with Network Extension. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Debugging a Network Extension Provider Debugging a Network Extension provider presents some challenges; its not as simple as choosing Product > Run in Xcode. Rather, you have to run the extension first and then choose Debug > Attach to Process. Attaching is simple, it’s the running part that causes all the problems. When you first start out it can be a challenge to get your extension to run at all. Add a First Light Log Point The first step is to check whether the system is actually starting your extension. My advice is to add a first light log point, a log point on the first line of code that you control. The exact mechanics of this depend on your development, your deployment target, and your NE provider’s packaging. In all cases, however, I recommend that you log to the system log. The system log has a bunch of cool features. If you’re curious, see Your Friend the System Log. The key advantage is that your log entries are mixed in with system log entries, which makes it easier to see what else is going on when your extension loads, or fails to load. IMPORTANT Use a unique subsystem and category for your log entries. This makes it easier to find them in the system log. For more information about Network Extension packaging options, see TN3134 Network Extension provider deployment. Logging in Swift If you’re using Swift, the best logging API depends on your deployment target. On modern systems — macOS 11 and later, iOS 14 and later, and aligned OS releases — it’s best to use the Logger API, which is shiny and new and super Swift friendly. For example: let log = Logger(subsystem: "com.example.galactic-mega-builds", category: "earth") let client = "The Mice" let answer = 42 log.log(level: .debug, "run complete, client: \(client), answer: \(answer, privacy: .private)") If you support older systems, use the older, more C-like API: let log = OSLog(subsystem: "com.example.galactic-mega-builds", category: "earth") let client = "The Mice" let answer = 42 os_log(.debug, log: log, "run complete, client: %@, answer: %{private}d", client as NSString, answer) Logging in C If you prefer a C-based language, life is simpler because you only have one choice: #import <os/log.h> os_log_t log = os_log_create("com.example.galactic-mega-builds", "earth"); const char * client = "The Mice"; int answer = 42; os_log_debug(log, "run complete, client: %s, answer: %{private}d", client, answer); Add a First Light Log Point to Your App Extension If your Network Extension provider is packaged as an app extension, the best place for your first light log point is an override of the provider’s initialiser. There are a variety of ways you could structure this but here’s one possibility: import NetworkExtension import os.log class PacketTunnelProvider: NEPacketTunnelProvider { static let log = Logger(subsystem: "com.example.myvpnapp", category: "packet-tunnel") override init() { self.log = Self.log log.log(level: .debug, "first light") super.init() } let log: Logger … rest of your code here … } This uses a Swift static property to ensure that the log is constructed in a race-free manner, something that’s handy for all sorts of reasons. It’s possible for your code to run before this initialiser — for example, if you have a C++ static constructor — but that’s something that’s best to avoid. Add a First Light Log Point to Your System Extension If your Network Extension provider is packaged as a system extension, add your first light log point to main.swift. Here’s one way you might structure that: import NetworkExtension func main() -> Never { autoreleasepool { let log = PacketTunnelProvider.log log.log(level: .debug, "first light") NEProvider.startSystemExtensionMode() } dispatchMain() } main() See how the main function gets the log object from the static property on PacketTunnelProvider. I told you that’d come in handy (-: Again, it’s possible for your code to run before this but, again, that’s something that’s best to avoid. App Extension Hints Both iOS and macOS allow you to package your Network Extension provider as an app extension. On iOS this is super reliable. I’ve never seen any weirdness there. That’s not true on macOS. macOS lets the user put apps anywhere; they don’t have to be placed in the Applications directory. macOS maintains a database, the Launch Services database, of all the apps it knows about and their capabilities. The app extension infrastructure uses that database to find and load app extensions. It’s not uncommon for this database to get confused, which prevents Network Extension from loading your provider’s app extension. This is particularly common on developer machines, where you are building and rebuilding your app over and over again. The best way to avoid problems is to have a single copy of your app extension’s container app on the system. So, while you’re developing your app extension, delete any other copies of your app that might be lying around. If you run into problems you may be able to fix them using: lsregister, to interrogate and manipulate the Launch Services database pluginkit, to interrogate and manipulate the app extension state [1] IMPORTANT Both of these tools are for debugging only; they are not considered API. Also, lsregister is not on the default path; find it at /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister. For more details about pluginkit, see the pluginkit man page. When debugging a Network Extension provider, add buttons to make it easy to save and remove your provider’s configuration. For example, if you’re working on a packet tunnel provider you might add: A Save Config button that calls the saveToPreferences(completionHandler:) method to save the tunnel configuration you want to test with A Remove Config button that calls the removeFromPreferences(completionHandler:) method to remove your tunnel configuration These come in handy when you want to start again from scratch. Just click Remove Config and then Save Config and you’ve wiped the slate clean. You don’t have to leave these buttons in your final product, but it’s good to have them during bring up. [1] This tool is named after the PluginKit framework, a private framework used to load this type of app extension. It’s distinct from the ExtensionKit framework which is a new, public API for managing extensions. System Extension Hints macOS allows you to package your Network Extension provider as a system extension. For this to work the container app must be in the Applications directory [1]. Copying it across each time you rebuild your app is a chore. To avoid that, add a Build post-action script: Select your app’s scheme and choose Product > Scheme > Edit Scheme. On the left, select Build. Click the chevron to disclose all the options. Select Post-actions. In the main area, click the add (+) button and select New Run Script Action. In the “Provide build settings from” popup, select your app target. In the script field, enter this script: ditto "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "/Applications/${FULL_PRODUCT_NAME}" Now, each time you build your app, this script will copy it to the Applications directory. Build your app now, both to confirm that this works and to enable the next step. The next issue you’ll find is that choosing Product > Run runs the app from the build products directory rather than the Applications directory. To fix that: Edit your app’s scheme again. On the left, select Run. In the main area, select the Info tab. From the Executable popup, choose Other. Select the copy of your app in the Applications directory. Now, when you choose Product > Run, Xcode will run that copy rather than the one in the build products directory. Neat-o! For your system extension to run your container app must activate it. As with the Save Config and Remote Config buttons described earlier, it’s good to add easy-to-access buttons to activate and deactivate your system extension. With an app extension the system automatically terminates your extension process when you rebuild it. This is not the case with a system extension; you’ll have to deactivate and then reactivate it each time. Each activation must be approved in System Settings > Privacy & Security. To make that easier, leave System Settings running all the time. This debug cycle leaves deactivated but not removed system extensions installed on your system. These go away when you restart, so do that from time to time. Once a day is just fine. macOS includes a tool, systemextensionctl, to interrogate and manipulate system extension state. The workflow described above does not require that you use it, but it’s good to keep in mind. Its man page is largely content free so run the tool with no arguments to get help. [1] Unless you disable System Integrity Protection, but who wants to do that? You Can Attach with the Debugger Once your extension is running, attach with the debugger using one of two commands: To attach to an app extension, choose Debug > Attach to Process > YourAppExName. To attach to a system extension, choose Debug > Attach to Process by PID or Name. Make sure to select Debug Process As root. System extensions run as root so the attach will fail if you select Debug Process As Me. But Should You? Debugging networking code with a debugger is less than ideal because it’s common for in-progress network requests to time out while you’re stopped in the debugger. Debugging Network Extension providers this way is especially tricky because of the extra steps you have to take to get your provider running. So, while you can attach with the debugger, and that’s a great option in some cases, it’s often better not to do that. Rather, consider the following approach: Write the core logic of your provider so that you can unit test each subsystem outside of the provider. This may require some scaffolding but the time you take to set that up will pay off once you encounter your first gnarly problem. Add good logging to your provider to help debug problems that show up during integration testing. I recommend that you treat your logging as a feature of your product. Carefully consider where to add log points and at what level to log. Check this logging code into your source code repository and ship it — or at least the bulk of it — as part of your final product. This logging will be super helpful when it comes to debugging problems that only show up in the field. Remember that, when using the system log, log points that are present but don’t actually log anything are very cheap. In most cases it’s fine to leave these in your final product. Now go back and read Your Friend the System Log because it’s full of useful hints and tips on how to use the system log to debug the really hard problems. General Hints and Tips Install the Network Diagnostics and VPN (Network Extension) profiles [1] on your test device. These enable more logging and, most critically, the recording of private data. For more info about that last point, see… you guessed it… Your Friend the System Log. Get these profiles from our Bug Reporting > Profiles and Logs page. When you’re bringing up a Network Extension provider, do your initial testing with a tiny test app. I regularly see folks start out by running Safari and that’s less than ideal. Safari is a huge app with lots of complexity, so if things go wrong it’s hard to tell where to look. I usually create a small test app to use during bring up. The exact function of this test app varies by provider type. For example: If I’m building a packet tunnel provider, I might have a test function that makes an outgoing TCP connection to an IP address. Once I get that working I add another function that makes an outgoing TCP connection to a DNS name. Then I start testing UDP. And so on. Similarly for a content filter, but then it makes sense to add a test that runs a request using URLSession and another one to bring up a WKWebView. If I’m building a DNS proxy provider, my test app might use CFHost to run a simple name-to-address query. Also, consider doing your bring up on the Mac even if your final target is iOS. macOS has a bunch of handy tools for debugging networking issues, including: dig for DNS queries nc for TCP and UDP connections netstat to display the state of the networking stack tcpdump for recording a packet trace [2] Read their respective man pages for all the details. On the other hand, the build / run / debug cycle is simpler on iOS than it is on macOS, especially when you’re building a system extension on macOS. Even if your ultimate goal is to build a macOS-only system extension, if your provider type supports app extension packaging then you should consider whether it makes sense to adopt that packaging just for to speed up your development. If you do decide to try this, be aware that a packaging change can affect your code. See Network Extension Provider Packaging for more on that. [1] The latter is not a profile on macOS, but just a set of instructions. [2] You can use an RVI packet trace on iOS but it’s an extra setup step. Revision History 2026-04-01 Added a suggestion about provider packaging to the General Hints and Tips section. 2023-12-15 Fixed a particularly egregious typo (and spelling error in a section title, no less!). 2023-04-02 Fixed one of the steps in Sytem Extension Hints.
0
0
4.2k
3h
Network Interface APIs
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" Network Interface APIs Most developers don’t need to interact directly with network interfaces. If you do, read this post for a summary of the APIs available to you. Before you read this, read Network Interface Concepts. Interface List The standard way to get a list of interfaces and their addresses is getifaddrs. To learn more about this API, see its man page. A network interface has four fundamental attributes: A set of flags — These are packed into a CUnsignedInt. The flags bits are declared in <net/if.h>, starting with IFF_UP. An interface type — See Network Interface Type, below. An interface index — Valid indexes are greater than 0. A BSD interface name. For example, an Ethernet interface might be called en0. The interface name is shared between multiple network interfaces running over a given hardware interface. For example, IPv4 and IPv6 running over that Ethernet interface will both have the name en0. WARNING BSD interface names are not considered API. There’s no guarantee, for example, that an iPhone’s Wi-Fi interface is en0. You can map between the last two using if_indextoname and if_nametoindex. See the if_indextoname man page for details. An interface may also have address information. If present, this always includes the interface address (ifa_addr) and the network mask (ifa_netmask). In addition: Broadcast-capable interfaces (IFF_BROADCAST) have a broadcast address (ifa_broadaddr, which is an alias for ifa_dstaddr). Point-to-point interfaces (IFF_POINTOPOINT) have a destination address (ifa_dstaddr). Calling getifaddrs from Swift is a bit tricky. For an example of this, see QSocket: Interfaces. IP Address List Once you have getifaddrs working, it’s relatively easy to manipulate the results to build a list of just IP addresses, a list of IP addresses for each interface, and so on. QSocket: Interfaces has some Swift snippets that show this. Interface List Updates The interface list can change over time. Hardware interfaces can be added and removed, network interfaces come up and go down, and their addresses can change. It’s best to avoid caching information from getifaddrs. If thats unavoidable, use the kNotifySCNetworkChange Darwin notification to update your cache. For information about registering for Darwin notifications, see the notify man page (in section 3). This notification just tells you that something has changed. It’s up to you to fetch the new interface list and adjust your cache accordingly. You’ll find that this notification is sometimes posted numerous times in rapid succession. To avoid unnecessary thrashing, debounce it. While the Darwin notification API is easy to call from Swift, Swift does not import kNotifySCNetworkChange. To fix that, define that value yourself, calling a C function to get the value: var kNotifySCNetworkChange: UnsafePointer<CChar> { networkChangeNotifyKey() } Here’s what that C function looks like: extern const char * networkChangeNotifyKey(void) { return kNotifySCNetworkChange; } Network Interface Type There are two ways to think about a network interface’s type. Historically there were a wide variety of weird and wonderful types of network interfaces. The following code gets this legacy value for a specific BSD interface name: func legacyTypeForInterfaceNamed(_ name: String) -> UInt8? { var addrList: UnsafeMutablePointer<ifaddrs>? = nil let err = getifaddrs(&addrList) // In theory we could check `errno` here but, honestly, what are gonna // do with that info? guard err >= 0, let first = addrList else { return nil } defer { freeifaddrs(addrList) } return sequence(first: first, next: { $0.pointee.ifa_next }) .compactMap { addr in guard let nameC = addr.pointee.ifa_name, name == String(cString: nameC), let sa = addr.pointee.ifa_addr, sa.pointee.sa_family == AF_LINK, let data = addr.pointee.ifa_data else { return nil } return data.assumingMemoryBound(to: if_data.self).pointee.ifi_type } .first } The values are defined in <net/if_types.h>, starting with IFT_OTHER. However, this value is rarely useful because many interfaces ‘look like’ Ethernet and thus have a type of IFT_ETHER. Network framework has the concept of an interface’s functional type. This is an indication of how the interface fits into the system. There are two ways to get an interface’s functional type: If you’re using Network framework and have an NWInterface value, get the type property. If not, call ioctl with a SIOCGIFFUNCTIONALTYPE request. The return values are defined in <net/if.h>, starting with IFRTYPE_FUNCTIONAL_UNKNOWN. Swift does not import SIOCGIFFUNCTIONALTYPE, so it’s best to write this code in a C: extern uint32_t functionalTypeForInterfaceNamed(const char * name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } struct ifreq ifr = {}; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); bool success = ioctl(fd, SIOCGIFFUNCTIONALTYPE, &ifr) >= 0; int junk = close(fd); assert(junk == 0); if ( ! success ) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } return ifr.ifr_ifru.ifru_functional_type; } Finally, TN3158 Resolving Xcode 15 device connection issues documents the SIOCGIFDIRECTLINK flag as a specific way to identify the network interfaces uses by Xcode for device connection traffic. Revision History 2025-12-10 Added info about SIOCGIFDIRECTLINK. 2023-07-19 First posted.
0
0
2.2k
Dec ’25
Accessory Setup Kit (BLE) not showing multiple options nor the advertising name
I'm developing an application using the accessory setup kit (BLE) on iOS 18+. An important aspect of the connection process is being able to find and choose the correct device. I noticed on iOS 18.2 that I was able to both scroll through the discovered accessories as well as view the advertised name. However, after upgrading to 18.7.2, only a single device is viewable and the advertised name is no longer available. Is there a trigger for this feature that I need to enable or was this "multiple discovery" feature removed? If so, why?
0
1
213
Oct ’25
Extra-ordinary Networking
Most apps perform ordinary network operations, like fetching an HTTP resource with URLSession and opening a TCP connection to a mail server with Network framework. These operations are not without their challenges, but they’re the well-trodden path. If your app performs ordinary networking, see TN3151 Choosing the right networking API for recommendations as to where to start. Some apps have extra-ordinary networking requirements. For example, apps that: Help the user configure a Wi-Fi accessory Require a connection to run over a specific interface Listen for incoming connections Building such an app is tricky because: Networking is hard in general. Apple devices support very dynamic networking, and your app has to work well in whatever environment it’s running in. Documentation for the APIs you need is tucked away in man pages and doc comments. In many cases you have to assemble these APIs in creative ways. If you’re developing an app with extra-ordinary networking requirements, this post is for you. Note If you have questions or comments about any of the topics discussed here, put them in a new thread here on DevForums. Make sure I see it by putting it in the App & System Services > Networking area. And feel free to add tags appropriate to the specific technology you’re using, like Foundation, CFNetwork, Network, or Network Extension. Links, Links, and More Links Each topic is covered in a separate post: The iOS Wi-Fi Lifecycle describes how iOS joins and leaves Wi-Fi networks. Understanding this is especially important if you’re building an app that works with a Wi-Fi accessory. Network Interface Concepts explains how Apple platforms manage network interfaces. If you’ve got this far, you definitely want to read this. Network Interface Techniques offers a high-level overview of some of the more common techniques you need when working with network interfaces. Network Interface APIs describes APIs and core techniques for working with network interfaces. It’s referenced by many other posts. Running an HTTP Request over WWAN explains why most apps should not force an HTTP request to run over WWAN, what they should do instead, and what to do if you really need that behaviour. If you’re building an iOS app with an embedded network server, see Showing Connection Information in an iOS Server for details on how to get the information to show to your user so they can connect to your server. Many folks run into trouble when they try to find the device’s IP address, or other seemingly simple things, like the name of the Wi-Fi interface. Don’t Try to Get the Device’s IP Address explains why these problems are hard, and offers alternative approaches that function correctly in all network environments. Similarly, folks also run into trouble when trying to get the host name. On Host Names explains why that’s more complex than you might think. If you’re working with broadcasts or multicasts, see Broadcasts and Multicasts, Hints and Tips. If you’re building an app that works with a Wi-Fi accessory, see Working with a Wi-Fi Accessory. If you’re trying to gather network interface statistics, see Network Interface Statistics. There are also some posts that are not part of this series but likely to be of interest if you’re working in this space: TN3179 Understanding local network privacy discusses the local network privacy feature. Calling BSD Sockets from Swift does what it says on the tin, that is, explains how to call BSD Sockets from Swift. When doing weird things with the network, you often find yourself having to use BSD Sockets, and that API is not easy to call from Swift. The code therein is primarily for the benefit of test projects, oh, and DevForums posts like these. TN3111 iOS Wi-Fi API overview is a critical resource if you’re doing Wi-Fi specific stuff on iOS. TLS For Accessory Developers tackles the tricky topic of how to communicate securely with a network-based accessory. A Peek Behind the NECP Curtain discusses NECP, a subsystem that control which programs have access to which network interfaces. Networking Resources has links to many other useful resources. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History 2025-07-31 Added a link to A Peek Behind the NECP Curtain. 2025-03-28 Added a link to On Host Names. 2025-01-16 Added a link to Broadcasts and Multicasts, Hints and Tips. Updated the local network privacy link to point to TN3179. Made other minor editorial changes. 2024-04-30 Added a link to Network Interface Statistics. 2023-09-14 Added a link to TLS For Accessory Developers. 2023-07-23 First posted.
0
0
5.7k
Jul ’25
Understanding Also-Ran Connections
Every now and again folks notice that Network framework seems to create an unexpected number of connections on the wire. This post explains why that happens and what you should do about it. If you have questions or comments, put them in a new thread here on the forums. Use the App & System Services > Networking topic area and the Network tag. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Understanding Also-Ran Connections Network framework implements the Happy Eyeballs algorithm. That might create more on-the-wire connections than you expect. There are two common places where folks notice this: When looking at a packet trace When implementing a listener Imagine that you’ve implemented a TCP server using NWListener and you connect to it from a client using NWConnection. In many situations there are multiple network paths between the client and the server. For example, on a local network there’s always at least two paths: the link-local IPv6 path and either an infrastructure IPv4 path or the link-local IPv4 path. When you start your NWConnection, Network framework’s Happy Eyeballs algorithm might [1] start a TCP connection for each of these paths. It then races those connections. The one that connects first is the ‘winner’, and Network framework uses that connection for your traffic. Once it has a winner, the other connections, the also-ran connections, are redundant, and Network framework just closes them. You can observe this behaviour on the client side by looking in the system log. Many Network framework log entries (subsystem com.apple.network) contain a connection identifier. For example C8 is the eighth connection started by this process. Each connection may have child connections (C8.1, C8.2, …) and grandchild connections (C8.1.1, C8.1.2, …), and so on. You’ll see state transitions for these child connections occurring in parallel. For example, the following log entries show that C8 is racing the connection of two grandchild connections, C8.1.1 and C8.1.2: type: debug time: 12:22:26.825331+0100 process: TestAlsoRanConnections subsystem: com.apple.network category: connection message: nw_socket_connect [C8.1.1:1] Calling connectx(…) type: debug time: 12:22:26.964150+0100 process: TestAlsoRanConnections subsystem: com.apple.network category: connection message: nw_socket_connect [C8.1.2:1] Calling connectx(…) Note For more information about accessing the system log, see Your Friend the System Log. You also see this on the server side, but in this case each connection is visible to your code. When you connect from the client, Network framework calls your listener’s new connection handler with multiple connections. One of those is the winning connection and you’ll receive traffic on it. The others are the also-ran connections, and they close promptly. IMPORTANT Depending on network conditions there may be no also-ran connections. Or there may be lots of them. If you want to test the also-ran connection case, use Network Link Conditioner to add a bunch of delay to your packets. You don’t need to write special code to handle also-ran connections. From the perspective of your listener, these are simply connections that open and then immediately close. There’s no difference between an also-ran connection and, say, a connection from a client that immediately crashes. Or a connection generated by someone doing a port scan. Your server must be resilient to such things. However, the presence of these also-ran connections can be confusing, especially if you’re just getting started with Network framework, and hence this post. [1] This is “might” because the exact behaviour depends on network conditions. More on that below.
0
0
164
Apr ’25
Broadcasts and Multicasts, Hints and Tips
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.
0
0
635
Sep ’25
On FTP
Questions about FTP crop up from time-to-time here on DevForums. In most cases I write a general “don’t use FTP” response, but I don’t have time to go into all the details. I’ve created this post as a place to collect all of those details, so I can reference them in other threads. IMPORTANT Apple’s official position on FTP is: All our FTP APIs have been deprecated, and you should avoid using deprecated APIs. Apple has been slowly removing FTP support from the user-facing parts of our system. The most recent example of this is that we removed the ftp command-line tool in macOS 10.13. You should avoid the FTP protocol and look to adopt more modern alternatives. The rest of this post is an informational explanation of the overall FTP picture. This post is locked so I can keep it focused. If you have questions or comments, please do create a new thread in the App & System Services > Networking subtopic and I’ll respond there. Don’t Use FTP FTP is a very old and very crufty protocol. Certain things that seem obvious to us now — like being able to create a GUI client that reliably shows a directory listing in a platform-independent manner — aren’t possible to do in FTP. However, by far the biggest problem with FTP is that it provides no security [1]. Specifically, the FTP protocol: Provides no on-the-wire privacy, so anyone can see the data you transfer Provides no client-authenticates-server authentication, so you have no idea whether you’re talking to the right server Provides no data integrity, allowing an attacker to munge your data in transit Transfers user names and passwords in the clear Using FTP for anonymous downloads may be acceptable (see the explanation below) but most other uses of FTP are completely inappropriate for the modern Internet. IMPORTANT You should only use FTP for anonymous downloads if you have an independent way to check the integrity of the data you’ve downloaded. For example, if you’re downloading a software update, you could use code signing to check its integrity. If you don’t check the integrity of the data you’ve downloaded, an attacker could substitute a malicious download instead. This would be especially bad in, say, the software update case. These fundamental problems with the FTP protocol mean that it’s not a priority for Apple. This is reflected in the available APIs, which is the subject of the next section. FTP APIs Apple provides two FTP APIs: All Apple platforms provide FTP downloads via URLSession. Most Apple platforms (everything except watchOS) support CFFTPStream, which allows for directory listings, downloads, uploads, and directory creation. All of these FTP APIs are now deprecated: URLSession was deprecated for the purposes of FTP in the 2022 SDKs (macOS 13, iOS 16, iPadOS 16, tvOS 16, watchOS 9) [2]. CFFTPStream was deprecated in the 2016 SDKs (macOS 10.11, iOS 9, iPadOS 9, tvOS 9). CFFTPStream still works about as well as it ever did, which is not particularly well. Specifically: There is at least one known crashing bug (r. 35745763), albeit one that occurs quite infrequently. There are clear implementation limitations — like the fact that CFFTPCreateParsedResourceListing assumes a MacRoman text encoding (r. 7420589) — that won’t be fixed. If you’re looking for an example of how to use these APIs, check out SimpleFTPSample. Note This sample hasn’t been updated since 2013 and is unlikely to ever be updated given Apple’s position on FTP. The FTP support in URLSession has significant limitations: It only supports FTP downloads; there’s no support for uploads or any other FTP operations. It doesn’t support resumable FTP downloads [3]. It doesn’t work in background sessions. That prevents it from running FTP downloads in the background on iOS. It’s only supported in classic loading mode. See the usesClassicLoadingMode property and the doc comments in <Foundation/NSURLSession.h>. If Apple’s FTP APIs are insufficient for your needs, you’ll need to write or acquire your own FTP library. Before you do that, however, consider switching to an alternative protocol. After all, if you’re going to go to the trouble of importing a large FTP library into your code base, you might as well import a library for a better protocol. The next section discusses some options in this space. Alternative Protocols There are numerous better alternatives to FTP: HTTPS is by far the best alternative to FTP, offering good security, good APIs on Apple platforms, good server support, and good network compatibility. Implementing traditional FTP operations over HTTPS can be a bit tricky. One possible way forward is to enable DAV extensions on the server. FTPS is FTP over TLS (aka SSL). While FTPS adds security to the protocol, which is very important, it still inherits many of FTP’s other problems. Personally I try to avoid this protocol. SFTP is a file transfer protocol that’s completely unrelated to FTP. It runs over SSH, making it a great alternative in many of the ad hoc setups that traditionally use FTP. Apple doesn’t have an API for either FTPS or SFTP, although on macOS you may be able to make some headway by invoking the sftp command-line tool. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] In another thread someone asked me about FTP’s other problems, those not related to security, so let’s talk about that. One of FTP’s implicit design goals was to provide cross-platform support that exposes the target platform. You can think of FTP as being kinda like telnet. When you telnet from Unix to VMS, it doesn’t aim to abstract away VMS commands, so that you can type Unix commands at the VMS prompt. Rather, you’re expected to run VMS commands. FTP is (a bit) like that. This choice made sense back when the FTP protocol was invented. Folks were expecting to use FTP via a command-line client, so there was a human in the loop. If they ran a command and it produced VMS-like output, that was fine because they knew that they were FTPing into a VMS machine. However, most users today are using GUI clients, and this design choice makes it very hard to create a general GUI client for FTP. Let’s consider the simple problem of getting the contents of a directory. When you send an FTP LIST command, the server would historically run the platform native directory list command and pipe the results back to you. To create a GUI client you have to parse that data to extract the file names. Doing that is a serious challenge. Indeed, just the first step, working out the text encoding, is a challenge. Many FTP servers use UTF-8, but some use ISO-Latin-1, some use other standard encodings, some use Windows code pages, and so on. I say “historically” above because there have been various efforts to standardise this stuff, both in the RFCs and in individual server implementations. However, if you’re building a general client you can’t rely on these efforts. After all, the reason why folks continue to use FTP is because of it widespread support. [2] To quote the macOS 13 Ventura Release Notes: FTP is deprecated for URLSession and related APIs. Please adopt modern secure networking protocols such as HTTPS. (92623659) [3] Although you can implement resumable downloads using the lower-level CFFTPStream API, courtesy of the kCFStreamPropertyFTPFileTransferOffset property. Revision History 2025-10-06 Explained that URLSession only supports FTP in classic loading mode. Made other minor editorial changes. 2024-04-15 Added a footnote about FTP’s other problems. Made other minor editorial changes. 2022-08-09 Noted that the FTP support in URLSession is now deprecated. Made other minor editorial changes. 2021-04-06 Fixed the formatting. Fixed some links. 2018-02-23 First posted.
0
0
5.8k
Oct ’25
A Peek Behind the NECP Curtain
From time to time the subject of NECP grows up, both here on DevForums and in DTS cases. I’ve posted about this before but I wanted to collect those tidbits into single coherent post. If you have questions or comments, start a new thread in the App & System Services > Networking subtopic and tag it with Network Extension. That way I’ll be sure to see it go by. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" A Peek Behind the NECP Curtain NECP stands for Network Extension Control Protocol. It’s a subsystem within the Apple networking stack that controls which programs have access to which network interfaces. It’s vitally important to the Network Extension subsystem, hence the name, but it’s used in many different places. Indeed, a very familiar example of its use is the Settings > Mobile Data [1] user interface on iOS. NECP has no explicit API, although there are APIs that are offer some insight into its state. Continuing the Settings > Mobile Data example above, there is a little-known API, CTCellularData in the Core Telephony framework, that returns whether your app has access to WWAN. Despite having no API, NECP is still relevant to developers. The Settings > Mobile Data example is one place where it affects app developers but it’s most important for Network Extension (NE) developers. A key use case for NECP is to prevent VPN loops. When starting an NE provider, the system configures the NECP policy for the NE provider’s process to prevent it from using a VPN interface. This means that you can safely open a network connection inside your VPN provider without having to worry about its traffic being accidentally routed back to you. This is why, for example, an NE packet tunnel provider can use any networking API it wants, including BSD Sockets, to run its connection without fear of creating a VPN loop [1]. One place that NECP shows up regularly is the system log. Next time you see a system log entry like this: type: debug time: 15:02:54.817903+0000 process: Mail subsystem: com.apple.network category: connection message: nw_protocol_socket_set_necp_attributes [C723.1.1:1] setsockopt 39 SO_NECP_ATTRIBUTES … you’ll at least know what the necp means (-: Finally, a lot of NECP infrastructure is in the Darwin open source. As with all things in Darwin, it’s fine to poke around and see how your favourite feature works, but do not incorporate any information you find into your product. Stuff you uncover by looking in Darwin is not considered API. [1] Settings > Cellular Data if you speak American (-: [2] Network Extension providers can call the createTCPConnection(to:enableTLS:tlsParameters:delegate:) method to create an NWTCPConnection [3] that doesn’t run through the tunnel. You can use that if it’s convenient but you don’t need to use it. [3] NWTCPConnection is now deprecated, but there are non-deprecated equivalents. For the full story, see NWEndpoint History and Advice. Revision History 2025-12-12 Replaced “macOS networking stack” with “Apple networking stack” to avoid giving the impression that this is all about macOS. Added a link to NWEndpoint History and Advice. Made other minor editorial changes. 2023-02-27 First posted.
0
0
2.6k
Dec ’25
My app attempts to use a socket to establish a connection with my external device, but it fails
My external device can generate a fixed Wi-Fi network. When I connect to this Wi-Fi using my iPhone 17 Pro Max (iOS version 26.0.1), and my app tries to establish a connection using the following method, this method returns -1 int connect(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(connect); However, when I use other phones, such as iPhone 12, iPhone 8, iPhone 11, etc., to connect to this external device, the above method always returns successfully, with the parameters passed to the method remaining the same. I also tried resetting the network settings on the iPhone 17 Pro Max (iOS version 26.0.1), but it still cannot establish a connection.
0
0
39
Oct ’25
iOS Network Signal Strength
This issue has cropped up many times here on DevForums. Someone recently opened a DTS tech support incident about it, and I used that as an opportunity to post a definitive response here. If you have questions or comments about this, start a new thread and tag it with Network so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" iOS Network Signal Strength The iOS SDK has no general-purpose API that returns Wi-Fi or cellular signal strength in real time. Given that this has been the case for more than 10 years, it’s safe to assume that it’s not an accidental omission but a deliberate design choice. For information about the Wi-Fi APIs that are available on iOS, see TN3111 iOS Wi-Fi API overview. Network performance Most folks who ask about this are trying to use the signal strength to estimate network performance. This is a technique that I specifically recommend against. That’s because it produces both false positives and false negatives: The network signal might be weak and yet your app has excellent connectivity. For example, an iOS device on stage at WWDC might have terrible WWAN and Wi-Fi signal but that doesn’t matter because it’s connected to the Ethernet. The network signal might be strong and yet your app has very poor connectivity. For example, if you’re on a train, Wi-Fi signal might be strong in each carriage but the overall connection to the Internet is poor because it’s provided by a single over-stretched WWAN. The only good way to determine whether connectivity is good is to run a network request and see how it performs. If you’re issuing a lot of requests, use the performance of those requests to build a running estimate of how well the network is doing. Indeed, Apple practices what we preach here: This is exactly how HTTP Live Streaming works. Remember that network performance can change from moment to moment. The user’s train might enter or leave a tunnel, the user might step into a lift, and so on. If you build code to estimate the network performance, make sure it reacts to such changes. Keeping all of the above in mind, iOS 26 beta has two new APIs related to this issue: Network framework now offers a linkQuality property. See this post for my take on how to use this effectively. The WirelessInsights framework can notify you of anticipated WWAN condition changes. But what about this code I found on the ’net? Over the years various folks have used various unsupported techniques to get around this limitation. If you find code on the ’net that, say, uses KVC to read undocumented properties, or grovels through system logs, or walks the view hierarchy of the status bar, don’t use it. Such techniques are unsupported and, assuming they haven’t broken yet, are likely to break in the future. But what about Hotspot Helper? Hotspot Helper does have an API to read Wi-Fi signal strength, namely, the signalStrength property. However, this is not a general-purpose API. Like the rest of Hotspot Helper, this is tied to the specific use case for which it was designed. This value only updates in real time for networks that your hotspot helper is managing, as indicated by the isChosenHelper property. But what about MetricKit? MetricKit is so cool. Amongst other things, it supports the MXCellularConditionMetric payload, which holds a summary of the cellular conditions while your app was running. However, this is not a real-time signal strength value. But what if I’m working for a carrier? This post is about APIs in the iOS SDK. If you’re working for a carrier, discuss your requirements with your carrier’s contact at Apple. Revision History 2025-07-02 Updated to cover new features in the iOS 16 beta. Made other minor editorial changes. 2022-12-01 First posted.
0
0
4.6k
Jul ’25
Network Extension Resources
General: Forums subtopic: App & System Services > Networking DevForums tag: Network Extension Network Extension framework documentation Routing your VPN network traffic article Filtering traffic by URL sample code Filtering Network Traffic sample code TN3120 Expected use cases for Network Extension packet tunnel providers technote TN3134 Network Extension provider deployment technote TN3165 Packet Filter is not API technote Network Extension and VPN Glossary forums post Debugging a Network Extension Provider forums post Exporting a Developer ID Network Extension forums post Network Extension Framework Entitlements forums post Network Extension vs ad hoc techniques on macOS forums post Network Extension Provider Packaging forums post NWEndpoint History and Advice forums post Extra-ordinary Networking forums post Wi-Fi management: Understanding NEHotspotConfigurationErrorInternal forums post See also Networking Resources for general networking resources, including information about Wi-Fi. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
0
0
3.2k
2w
NSURLSession’s Resume Rate Limiter
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)
0
0
13k
Jul ’25
Understanding NEHotspotConfigurationErrorInternal
Error 8 in the NEHotspotConfigurationError domain is .internal, aka NEHotspotConfigurationErrorInternal. This error typically indicates that something went wrong in some sort of expected way, but we decided not to surface the exact cause [1]. This has come up a bunch of times before on the forums, and I have various titbits to share. To start, I want to address some specific cases: You’ll see this error if your app isn’t signed with the com.apple.developer.networking.HotspotConfiguration entitlement. To fix this, use Xcode’s Signing & Capabilities editor to add the Hotspot capability to your app. Historically developers reported a situation where once they encountered the error it would show up consistently, but then it would go away on restarting the device. If you see behaviour like that, that’s definitely a bug and I encourage you to file it as such. I have more about filing such bugs in Filing a Wi-Fi Bug Report. Of course, you have to wait to reproduce the error again before you’ll be able to file that bug, because the act of restarting cleared the issue. I’ve seen reports where such problems only occur on a specific type of device, for example, on iPhone 16 but not on earlier or later iPhones. That’s definitely something that Apple should investigate, and I recommend that you file a bug about it. If the problem is being reported by your users but you can’t reproduce it yourself, consider the various suggestions in Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem. Assuming you’re still here (-: the next step is to determine whether the problem is specific to NEHotspotConfigurationManager or not. Try joining the accessory’s network from Settings > Wi-Fi. Does that also have problems? If so, that’s not something we can help you with here on the forums. The focus of the Apple Developer Forums is primarily to help developers with the APIs in Apple’s various platform SDKs [2]. We’re not set up to help accessory developers with Wi-Fi issues. However, there are still things you can do, as I explain in Filing a Wi-Fi Bug Report. At this point you have an error that: Persists across restarts Happens with all Apple devices You can reproduce Only affects NEHotspotConfigurationManager If that’s correct then there are a couple of things you might look at: Coerce the error to an NSError and print that. Does it reveal anything interesting? Also check the underlying error property (NSUnderlyingErrorKey) for hints. When reproducing the error, monitor the system log for log entries in the com.apple.networkextension subsystem. Do those offer any clues? Note For lots of hints and tips about the system log, see Your Friend the System Log. And finally, if you have questions about this case, feel free to start a thread here on the forums and we’ll try to help you out. Put it in the App & System Services > Networking subtopic and tag it with Network Extension. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] There’s also the .unknown error. See this post for a brief summary of the difference. [2] And with Apple tools and some developer-oriented services. Revision History 2026-03-18 Added a missing entitlement bullet to the specific case list. 2026-03-17 First posted.
0
0
60
2w
About the Relay payload in iOS configuration profiles
Are the network relays introduced in 2023 and https://developer.apple.com/videos/play/wwdc2023/10002/ the same thing as the Private Relay introduced in 2021? https://developer.apple.com/videos/play/wwdc2021/10096/ We are considering verifying the relay function, but we are not sure whether they are the same function or different functions. https://developer.apple.com/documentation/devicemanagement/relay?language=objc
0
0
55
Apr ’25
CoreBluetooth and BLE AdvertisementData
Hi, We're receiving data via centralManager.centralManager.scanForPeripherals, with no options or filtering (for now), and in the func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) callback, we get advertisementData for each bluetooth device found. But, I know one of my BLE devices is sending an Eddystone TLM payload, which generally is received into the kCBAdvDataServiceData part of the advertisementData dictionary, but, it doesn't show up. What is happening however (when comparing to other devices that do show that payload), is I've noticed the "isConnectable" part is false, and others have it true. Technically we're not "connecting" as such as we're simply reading passive advertisement data, but does that have any bearing on how CoreBluetooth decides to build up it's AdvertisementData response? Example (with serviceData; and I know this has Eddystone TLM) ["kCBAdvDataLocalName": FSC-BP105N, "kCBAdvDataRxPrimaryPHY": 1, "kCBAdvDataServiceUUIDs": <__NSArrayM 0x300b71f80>( FEAA, FEF5 ) , "kCBAdvDataTimestamp": 773270526.26279, "kCBAdvDataServiceData": { FFF0 = {length = 11, bytes = 0x36021892dc0d3015aeb164}; FEAA = {length = 14, bytes = 0x20000be680000339ffa229bbce8a}; }, "kCBAdvDataRxSecondaryPHY": 0, "kCBAdvDataIsConnectable": 1] Vs This also has Eddystone TLM configured ["kCBAdvDataLocalName": 100FA9FD-7000-1000, "kCBAdvDataIsConnectable": 0, "kCBAdvDataRxPrimaryPHY": 1, "kCBAdvDataRxSecondaryPHY": 0, "kCBAdvDataTimestamp": 773270918.97273] Any insight would be great to understand if the presence of other flags drive the exposure of ServiceData or not...
0
0
143
Jul ’25
iOS 26 Crash: _xzm_xzone_malloc_freelist_outlined in com.apple.network.connections
Hello Apple Support Team, We are seeing a production crash on iOS 26 devices that appears to originate from Apple system frameworks rather than application code. Crash Summary Crash signature: _xzm_xzone_malloc_freelist_outlined Crashed thread: com.apple.network.connections Frameworks involved: CFNetwork, Security, libdispatch, libsystem_malloc Affected OS: iOS 26.x App built with: Xcode 16 Devices: Multiple models (not device-specific) Reproducibility: Intermittent, higher frequency during app launch / background networking Observed Stack Trace (top frames) _xzm_xzone_malloc_freelist_outlined dispatch_data_create_alloc xpc_data_deserialize SecTrustEvaluateIfNecessary CFNetwork HTTPProtocol / HTTP3Connection com.apple.network.connections App Context The app uses URLSession for networking. Multiple third-party SDKs are integrated (Firebase Analytics, Dynatrace, Appsflyer, and similar analytics/monitoring SDKs). These SDKs perform concurrent background network requests, especially during app launch and foreground transitions. No unsafe memory operations (manual malloc/free, unsafe pointers, or custom networking stacks) are used in the app code. Key Observations The crash is predominantly observed on iOS 26 and not on earlier iOS versions. Stack traces do not include application symbols. Disabling or delaying analytics SDK initialization significantly reduces the crash rate. Reducing concurrent network requests and limiting HTTP/3 usage also mitigates the issue. This suggests a potential regression in CFNetwork / Network.framework / HTTP/3 handling combined with the new memory allocator (xzone) on iOS 26. Impact Random app termination during background networking. Occurs without a clear deterministic repro path, making it difficult to fully mitigate at the app level. Request Could you please help investigate whether this is a known iOS 26 issue related to: HTTP/3 / QUIC networking XPC deserialization Memory allocation in the new xzone allocator High-concurrency network requests We would appreciate guidance on: Recommended mitigations Whether this issue is already tracked internally Any best practices for apps integrating multiple analytics SDKs on iOS 26 Crash logs and additional diagnostics can be provided if needed. Thank you for your support. Best regards, Dhananjay
0
1
193
Feb ’26
Pair iOS Central with MacOS Peripheral for encrypted characteristic
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?
0
0
90
Feb ’26
TLS for App Developers
Transport Layer Security (TLS) is the most important security protocol on the Internet today. Most notably, TLS puts the S into HTTPS, adding security to the otherwise insecure HTTP protocol. IMPORTANT TLS is the successor to the Secure Sockets Layer (SSL) protocol. SSL is no longer considered secure and it’s now rarely used in practice, although many folks still say SSL when they mean TLS. TLS is a complex protocol. Much of that complexity is hidden from app developers but there are places where it’s important to understand specific details of the protocol in order to meet your requirements. This post explains the fundamentals of TLS, concentrating on the issues that most often confuse app developers. Note The focus of this is TLS-PKI, where PKI stands for public key infrastructure. This is the standard TLS as deployed on the wider Internet. There’s another flavour of TLS, TLS-PSK, where PSK stands for pre-shared key. This has a variety of uses, but an Apple platforms we most commonly see it with local traffic, for example, to talk to a Wi-Fi based accessory. For more on how to use TLS, both TLS-PKI and TLS-PSK, in a local context, see TLS For Accessory Developers. Server Certificates For standard TLS to work the server must have a digital identity, that is, the combination of a certificate and the private key matching the public key embedded in that certificate. TLS Crypto Magic™ ensures that: The client gets a copy of the server’s certificate. The client knows that the server holds the private key matching the public key in that certificate. In a typical TLS handshake the server passes the client a list of certificates, where item 0 is the server’s certificate (the leaf certificate), item N is (optionally) the certificate of the certificate authority that ultimately issued that certificate (the root certificate), and items 1 through N-1 are any intermediate certificates required to build a cryptographic chain of trust from 0 to N. Note The cryptographic chain of trust is established by means of digital signatures. Certificate X in the chain is issued by certificate X+1. The owner of certificate X+1 uses their private key to digitally sign certificate X. The client verifies this signature using the public key embedded in certificate X+1. Eventually this chain terminates in a trusted anchor, that is, a certificate that the client trusts by default. Typically this anchor is a self-signed root certificate from a certificate authority. Note Item N is optional for reasons I’ll explain below. Also, the list of intermediate certificates may be empty (in the case where the root certificate directly issued the leaf certificate) but that’s uncommon for servers in the real world. Once the client gets the server’s certificate, it evaluates trust on that certificate to confirm that it’s talking to the right server. There are three levels of trust evaluation here: Basic X.509 trust evaluation checks that there’s a cryptographic chain of trust from the leaf through the intermediates to a trusted root certificate. The client has a set of trusted root certificates built in (these are from well-known certificate authorities, or CAs), and a site admin can add more via a configuration profile. This step also checks that none of the certificates have expired, and various other more technical criteria (like the Basic Constraints extension). Note This explains why the server does not have to include the root certificate in the list of certificates it passes to the client; the client has to have the root certificate installed if trust evaluation is to succeed. In addition, TLS trust evaluation (per RFC 2818) checks that the DNS name that you connected to matches the DNS name in the certificate. Specifically, the DNS name must be listed in the Subject Alternative Name extension. Note The Subject Alternative Name extension can also contain IP addresses, although that’s a much less well-trodden path. Also, historically it was common to accept DNS names in the Common Name element of the Subject but that is no longer the case on Apple platforms. App Transport Security (ATS) adds its own security checks. Basic X.509 and TLS trust evaluation are done for all TLS connections. ATS is only done on TLS connections made by URLSession and things layered on top URLSession (like WKWebView). In many situations you can override trust evaluation; for details, see Technote 2232 HTTPS Server Trust Evaluation). Such overrides can either tighten or loosen security. For example: You might tighten security by checking that the server certificate was issued by a specific CA. That way, if someone manages to convince a poorly-managed CA to issue them a certificate for your server, you can detect that and fail. You might loosen security by adding your own CA’s root certificate as a trusted anchor. IMPORTANT If you rely on loosened security you have to disable ATS. If you leave ATS enabled, it requires that the default server trust evaluation succeeds regardless of any customisations you do. Mutual TLS The previous section discusses server trust evaluation, which is required for all standard TLS connections. That process describes how the client decides whether to trust the server. Mutual TLS (mTLS) is the opposite of that, that is, it’s the process by which the server decides whether to trust the client. Note mTLS is commonly called client certificate authentication. I avoid that term because of the ongoing industry-wide confusion between certificates and digital identities. While it’s true that, in mTLS, the server authenticates the client certificate, to set this up on the client you need a digital identity, not a certificate. mTLS authentication is optional. The server must request a certificate from the client and the client may choose to supply one or not (although if the server requests a certificate and the client doesn’t supply one it’s likely that the server will then fail the connection). At the TLS protocol level this works much like it does with the server certificate. For the client to provide this certificate it must apply a digital identity, known as the client identity, to the connection. TLS Crypto Magic™ assures the server that, if it gets a certificate from the client, the client holds the private key associated with that certificate. Where things diverge is in trust evaluation. Trust evaluation of the client certificate is done on the server, and the server uses its own rules to decided whether to trust a specific client certificate. For example: Some servers do basic X.509 trust evaluation and then check that the chain of trust leads to one specific root certificate; that is, a client is trusted if it holds a digital identity whose certificate was issued by a specific CA. Some servers just check the certificate against a list of known trusted client certificates. When the client sends its certificate to the server it actually sends a list of certificates, much as I’ve described above for the server’s certificates. In many cases the client only needs to send item 0, that is, its leaf certificate. That’s because: The server already has the intermediate certificates required to build a chain of trust from that leaf to its root. There’s no point sending the root, as I discussed above in the context of server trust evaluation. However, there are no hard and fast rules here; the server does its client trust evaluation using its own internal logic, and it’s possible that this logic might require the client to present intermediates, or indeed present the root certificate even though it’s typically redundant. If you have problems with this, you’ll have to ask the folks running the server to explain its requirements. Note If you need to send additional certificates to the server, pass them to the certificates parameter of the method you use to create your URLCredential (typically init(identity:certificates:persistence:)). One thing that bears repeating is that trust evaluation of the client certificate is done on the server, not the client. The client doesn’t care whether the client certificate is trusted or not. Rather, it simply passes that certificate the server and it’s up to the server to make that decision. When a server requests a certificate from the client, it may supply a list of acceptable certificate authorities [1]. Safari uses this to filter the list of client identities it presents to the user. If you are building an HTTPS server and find that Safari doesn’t show the expected client identity, make sure you have this configured correctly. If you’re building an iOS app and want to implement a filter like Safari’s, get this list using: The distinguishedNames property, if you’re using URLSession The sec_protocol_metadata_access_distinguished_names routine, if you’re using Network framework [1] See the certificate_authorities field in Section 7.4.4 of RFC 5246, and equivalent features in other TLS versions. Self-Signed Certificates Self-signed certificates are an ongoing source of problems with TLS. There’s only one unequivocally correct place to use a self-signed certificate: the trusted anchor provided by a certificate authority. One place where a self-signed certificate might make sense is in a local environment, that is, securing a connection between peers without any centralised infrastructure. However, depending on the specific circumstances there may be a better option. TLS For Accessory Developers discusses this topic in detail. Finally, it’s common for folks to use self-signed certificates for testing. I’m not a fan of that approach. Rather, I recommend the approach described in QA1948 HTTPS and Test Servers. For advice on how to set that up using just your Mac, see TN2326 Creating Certificates for TLS Testing. TLS Standards RFC 6101 The Secure Sockets Layer (SSL) Protocol Version 3.0 (historic) RFC 2246 The TLS Protocol Version 1.0 RFC 4346 The Transport Layer Security (TLS) Protocol Version 1.1 RFC 5246 The Transport Layer Security (TLS) Protocol Version 1.2 RFC 8446 The Transport Layer Security (TLS) Protocol Version 1.3 RFC 4347 Datagram Transport Layer Security RFC 6347 Datagram Transport Layer Security Version 1.2 RFC 9147 The Datagram Transport Layer Security (DTLS) Protocol Version 1.3 Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History: 2025-11-21 Clearly defined the terms TLS-PKI and TLS-PSK. 2024-03-19 Adopted the term mutual TLS in preference to client certificate authentication throughout, because the latter feeds into the ongoing certificate versus digital identity confusion. Defined the term client identity. Added the Self-Signed Certificates section. Made other minor editorial changes. 2023-02-28 Added an explanation mTLS acceptable certificate authorities. 2022-12-02 Added links to the DTLS RFCs. 2022-08-24 Added links to the TLS RFCs. Made other minor editorial changes. 2022-06-03 Added a link to TLS For Accessory Developers. 2021-02-26 Fixed the formatting. Clarified that ATS only applies to URLSession. Minor editorial changes. 2020-04-17 Updated the discussion of Subject Alternative Name to account for changes in the 2019 OS releases. Minor editorial updates. 2018-10-29 Minor editorial updates. 2016-11-11 First posted.
0
0
8.3k
Nov ’25
NWEndpoint History and Advice
The path from Network Extension’s in-provider networking APIs to Network framework has been long and somewhat rocky. The most common cause of confusion is NWEndpoint, where the same name can refer to two completely different types. I’ve helped a bunch of folks with this over the years, and I’ve decided to create this post to collect together all of those titbits. If you have questions or comments, please put them in a new thread. Put it in the App & System Services > Networking subtopic and tag it with Network Extension. That way I’ll be sure to see it go by. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" NWEndpoint History and Advice A tale that spans three APIs, two languages, and ten years. The NWEndpoint type has a long and complex history, and if you’re not aware of that history you can bump into weird problems. The goal of this post is to explain the history and then offer advice on how to get around specific problems. IMPORTANT This post focuses on NWEndpoint, because that’s the type that causes the most problems, but there’s a similar situation with NWPath. The History In iOS 9 Apple introduced the Network Extension (NE) framework, which offers a convenient way for developers to create a custom VPN transport. Network Extension types all have the NE prefix. Note I’m gonna use iOS versions here, just to keep the text simple. If you’re targeting some other platform, use this handy conversion table: iOS | macOS | tvOS | watchOS | visionOS --- + ----- + ---- + ------- + -------- 9 | 10.11 | 9 | 2 | - 12 | 10.14 | 12 | 5 | - 18 | 15 | 18 | 11 | 2 At that time we also introduced in-provider networking APIs. The idea was that an NE provider could uses these Objective-C APIs to communicate with its VPN server, and thereby avoiding a bunch of ugly BSD Sockets code. The in-provider networking APIs were limited to NE providers. Specifically, the APIs to construct an in-provider connection were placed on types that were only usable within an NE provider. For example, a packet tunnel provider could create a NWTCPConnection object by calling -createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate:] and -createTCPConnectionThroughTunnelToEndpoint:enableTLS:TLSParameters:delegate:, which are both methods on NEPacketTunnelProvider. These in-provider networking APIs came with a number of ancillary types, including NWEndpoint and NWPath. At the time we thought that we might promote these in-provider networking APIs to general-purpose networking APIs. That’s why the APIs use the NW prefix. For example, it’s NWTCPConnection, not NETCPConnection. However, plans changed. In iOS 12 Apple shipped Network framework as our recommended general-purpose networking API. This actually includes two APIs: A Swift API that follows Swift conventions, for example, the connection type is called NWConnection A C API that follows C conventions, for example, the connection type is called nw_connection_t These APIs follow similar design patterns to the in-provider networking API, and thus have similar ancillary types. Specifically, there are an NWEndpoint and nw_endpoint_t types, both of which perform a similar role to the NWEndpoint type in the in-provider networking API. This was a source of some confusion in Swift, because the name NWEndpoint could refer to either the Network framework type or the Network Extension framework type, depending on what you’d included. Fortunately you could get around this by qualifying the type as either Network.NWEndpoint or NetworkExtension.NWEndpoint. The arrival of Network framework meant that it no longer made sense to promote the in-provider networking APIs to general-purposes networking APIs. The in-provider networking APIs were on the path to deprecation. However, deprecating these APIs was actually quite tricky. Network Extension framework uses these APIs in a number of interesting ways, and so deprecating them required adding replacements. In addition, we’d needed different replacements for Swift and Objective-C, because Network framework has separate APIs for Swift and C-based languages. In iOS 18 we tackled that problem head on. To continue the NWTCPConnection example above, we replaced: -createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate:] with nw_connection_t -createTCPConnectionThroughTunnelToEndpoint:enableTLS:TLSParameters:delegate: with nw_connection_t combined with a new virtualInterface property on NEPacketTunnelProvider Of course that’s the Objective-C side of things. In Swift, the replacement is NWConnection rather than nw_connection_t, and the type of the virtualInterface property is NWInterface rather than nw_interface_t. But that’s not the full story. For the two types that use the same name in both frameworks, NWEndpoint and NWPath, we decided to use this opportunity to sort out that confusion. To see how we did that, check out the <NetworkExtension/NetworkExtension.apinotes> file in the SDK. Focusing on NWEndpoint for the moment, you’ll find two entries: … - Name: NWEndpoint SwiftPrivate: true … SwiftVersions: - Version: 5.0 … - Name: NWEndpoint SwiftPrivate: false … The first entry applies when you’re building with the Swift 6 language mode. This marks the type as SwiftPrivate, which means that Swift imports it as __NWEndpoint. That frees up the NWEndpoint name to refer exclusively to the Network framework type. The second entry applies when you’re building with the Swift 5 language mode. It marks the type as not SwiftPrivate. This is a compatible measure to ensure that code written for Swift 5 continues to build. The Advice This sections discusses specific cases in this transition. NWEndpoint and NWPath In Swift 5 language mode, NWEndpoint and NWPath might refer to either framework, depending on what you’ve imported. Add a qualifier if there’s any ambiguity, for example, Network.NWEndpoint or NetworkExtension.NWEndpoint. In Swift 6 language mode, NWEndpoint and NWPath always refer to the Network framework type. Add a __ prefix to get to the Network Extension type. For example, use NWEndpoint for the Network framework type and __NWEndpoint for the Network Extension type. Direct and Through-Tunnel TCP Connections in Swift To create a connection directly, simply create an NWConnection. This support both TCP and UDP, with or without TLS. To create a connection through the tunnel, replace code like this: let c = self.createTCPConnectionThroughTunnel(…) with code like this: let params = NWParameters.tcp params.requiredInterface = self.virtualInterface let c = NWConnection(to: …, using: params) This is for TCP but the same basic process applies to UDP. UDP and App Proxies in Swift If you’re building an app proxy, transparent proxy, or DNS proxy in Swift and need to handle UDP flows using the new API, adopt the NEAppProxyUDPFlowHandling protocol. So, replace code like this: class AppProxyProvider: NEAppProxyProvider { … override func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteEndpoint remoteEndpoint: NWEndpoint) -> Bool { … } } with this: class AppProxyProvider: NEAppProxyProvider, NEAppProxyUDPFlowHandling { … func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteFlowEndpoint remoteEndpoint: NWEndpoint) -> Bool { … } } Creating a Network Rule To create an NWHostEndpoint, replace code like this: let ep = NWHostEndpoint(hostname: "1.2.3.4", port: "12345") let r = NENetworkRule(destinationHost: ep, protocol: .TCP) with this: let ep = NWEndpoint.hostPort(host: "1.2.3.4", port: 12345) let r = NENetworkRule(destinationHostEndpoint: ep, protocol: .TCP) Note how the first label of the initialiser has changed from destinationHost to destinationHostEndpoint.
0
0
270
Jul ’25
Network Extension Framework Entitlements
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.
0
0
23k
Nov ’25
Networking Resources
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.
Replies
0
Boosts
0
Views
4.1k
Activity
2w
Debugging a Network Extension Provider
I regularly see folks struggle to debug their Network Extension providers. For an app, and indeed various app extensions, debugging is as simple as choosing Product > Run in Xcode. That’s not the case with a Network Extension provider, so I thought I’d collect together some hints and tips to help you get started. If you have any comments or questions, create a new thread here on DevForums. Put it in the App & System Services > Networking and tag it with Network Extension. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Debugging a Network Extension Provider Debugging a Network Extension provider presents some challenges; its not as simple as choosing Product > Run in Xcode. Rather, you have to run the extension first and then choose Debug > Attach to Process. Attaching is simple, it’s the running part that causes all the problems. When you first start out it can be a challenge to get your extension to run at all. Add a First Light Log Point The first step is to check whether the system is actually starting your extension. My advice is to add a first light log point, a log point on the first line of code that you control. The exact mechanics of this depend on your development, your deployment target, and your NE provider’s packaging. In all cases, however, I recommend that you log to the system log. The system log has a bunch of cool features. If you’re curious, see Your Friend the System Log. The key advantage is that your log entries are mixed in with system log entries, which makes it easier to see what else is going on when your extension loads, or fails to load. IMPORTANT Use a unique subsystem and category for your log entries. This makes it easier to find them in the system log. For more information about Network Extension packaging options, see TN3134 Network Extension provider deployment. Logging in Swift If you’re using Swift, the best logging API depends on your deployment target. On modern systems — macOS 11 and later, iOS 14 and later, and aligned OS releases — it’s best to use the Logger API, which is shiny and new and super Swift friendly. For example: let log = Logger(subsystem: "com.example.galactic-mega-builds", category: "earth") let client = "The Mice" let answer = 42 log.log(level: .debug, "run complete, client: \(client), answer: \(answer, privacy: .private)") If you support older systems, use the older, more C-like API: let log = OSLog(subsystem: "com.example.galactic-mega-builds", category: "earth") let client = "The Mice" let answer = 42 os_log(.debug, log: log, "run complete, client: %@, answer: %{private}d", client as NSString, answer) Logging in C If you prefer a C-based language, life is simpler because you only have one choice: #import <os/log.h> os_log_t log = os_log_create("com.example.galactic-mega-builds", "earth"); const char * client = "The Mice"; int answer = 42; os_log_debug(log, "run complete, client: %s, answer: %{private}d", client, answer); Add a First Light Log Point to Your App Extension If your Network Extension provider is packaged as an app extension, the best place for your first light log point is an override of the provider’s initialiser. There are a variety of ways you could structure this but here’s one possibility: import NetworkExtension import os.log class PacketTunnelProvider: NEPacketTunnelProvider { static let log = Logger(subsystem: "com.example.myvpnapp", category: "packet-tunnel") override init() { self.log = Self.log log.log(level: .debug, "first light") super.init() } let log: Logger … rest of your code here … } This uses a Swift static property to ensure that the log is constructed in a race-free manner, something that’s handy for all sorts of reasons. It’s possible for your code to run before this initialiser — for example, if you have a C++ static constructor — but that’s something that’s best to avoid. Add a First Light Log Point to Your System Extension If your Network Extension provider is packaged as a system extension, add your first light log point to main.swift. Here’s one way you might structure that: import NetworkExtension func main() -> Never { autoreleasepool { let log = PacketTunnelProvider.log log.log(level: .debug, "first light") NEProvider.startSystemExtensionMode() } dispatchMain() } main() See how the main function gets the log object from the static property on PacketTunnelProvider. I told you that’d come in handy (-: Again, it’s possible for your code to run before this but, again, that’s something that’s best to avoid. App Extension Hints Both iOS and macOS allow you to package your Network Extension provider as an app extension. On iOS this is super reliable. I’ve never seen any weirdness there. That’s not true on macOS. macOS lets the user put apps anywhere; they don’t have to be placed in the Applications directory. macOS maintains a database, the Launch Services database, of all the apps it knows about and their capabilities. The app extension infrastructure uses that database to find and load app extensions. It’s not uncommon for this database to get confused, which prevents Network Extension from loading your provider’s app extension. This is particularly common on developer machines, where you are building and rebuilding your app over and over again. The best way to avoid problems is to have a single copy of your app extension’s container app on the system. So, while you’re developing your app extension, delete any other copies of your app that might be lying around. If you run into problems you may be able to fix them using: lsregister, to interrogate and manipulate the Launch Services database pluginkit, to interrogate and manipulate the app extension state [1] IMPORTANT Both of these tools are for debugging only; they are not considered API. Also, lsregister is not on the default path; find it at /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister. For more details about pluginkit, see the pluginkit man page. When debugging a Network Extension provider, add buttons to make it easy to save and remove your provider’s configuration. For example, if you’re working on a packet tunnel provider you might add: A Save Config button that calls the saveToPreferences(completionHandler:) method to save the tunnel configuration you want to test with A Remove Config button that calls the removeFromPreferences(completionHandler:) method to remove your tunnel configuration These come in handy when you want to start again from scratch. Just click Remove Config and then Save Config and you’ve wiped the slate clean. You don’t have to leave these buttons in your final product, but it’s good to have them during bring up. [1] This tool is named after the PluginKit framework, a private framework used to load this type of app extension. It’s distinct from the ExtensionKit framework which is a new, public API for managing extensions. System Extension Hints macOS allows you to package your Network Extension provider as a system extension. For this to work the container app must be in the Applications directory [1]. Copying it across each time you rebuild your app is a chore. To avoid that, add a Build post-action script: Select your app’s scheme and choose Product > Scheme > Edit Scheme. On the left, select Build. Click the chevron to disclose all the options. Select Post-actions. In the main area, click the add (+) button and select New Run Script Action. In the “Provide build settings from” popup, select your app target. In the script field, enter this script: ditto "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "/Applications/${FULL_PRODUCT_NAME}" Now, each time you build your app, this script will copy it to the Applications directory. Build your app now, both to confirm that this works and to enable the next step. The next issue you’ll find is that choosing Product > Run runs the app from the build products directory rather than the Applications directory. To fix that: Edit your app’s scheme again. On the left, select Run. In the main area, select the Info tab. From the Executable popup, choose Other. Select the copy of your app in the Applications directory. Now, when you choose Product > Run, Xcode will run that copy rather than the one in the build products directory. Neat-o! For your system extension to run your container app must activate it. As with the Save Config and Remote Config buttons described earlier, it’s good to add easy-to-access buttons to activate and deactivate your system extension. With an app extension the system automatically terminates your extension process when you rebuild it. This is not the case with a system extension; you’ll have to deactivate and then reactivate it each time. Each activation must be approved in System Settings > Privacy & Security. To make that easier, leave System Settings running all the time. This debug cycle leaves deactivated but not removed system extensions installed on your system. These go away when you restart, so do that from time to time. Once a day is just fine. macOS includes a tool, systemextensionctl, to interrogate and manipulate system extension state. The workflow described above does not require that you use it, but it’s good to keep in mind. Its man page is largely content free so run the tool with no arguments to get help. [1] Unless you disable System Integrity Protection, but who wants to do that? You Can Attach with the Debugger Once your extension is running, attach with the debugger using one of two commands: To attach to an app extension, choose Debug > Attach to Process > YourAppExName. To attach to a system extension, choose Debug > Attach to Process by PID or Name. Make sure to select Debug Process As root. System extensions run as root so the attach will fail if you select Debug Process As Me. But Should You? Debugging networking code with a debugger is less than ideal because it’s common for in-progress network requests to time out while you’re stopped in the debugger. Debugging Network Extension providers this way is especially tricky because of the extra steps you have to take to get your provider running. So, while you can attach with the debugger, and that’s a great option in some cases, it’s often better not to do that. Rather, consider the following approach: Write the core logic of your provider so that you can unit test each subsystem outside of the provider. This may require some scaffolding but the time you take to set that up will pay off once you encounter your first gnarly problem. Add good logging to your provider to help debug problems that show up during integration testing. I recommend that you treat your logging as a feature of your product. Carefully consider where to add log points and at what level to log. Check this logging code into your source code repository and ship it — or at least the bulk of it — as part of your final product. This logging will be super helpful when it comes to debugging problems that only show up in the field. Remember that, when using the system log, log points that are present but don’t actually log anything are very cheap. In most cases it’s fine to leave these in your final product. Now go back and read Your Friend the System Log because it’s full of useful hints and tips on how to use the system log to debug the really hard problems. General Hints and Tips Install the Network Diagnostics and VPN (Network Extension) profiles [1] on your test device. These enable more logging and, most critically, the recording of private data. For more info about that last point, see… you guessed it… Your Friend the System Log. Get these profiles from our Bug Reporting > Profiles and Logs page. When you’re bringing up a Network Extension provider, do your initial testing with a tiny test app. I regularly see folks start out by running Safari and that’s less than ideal. Safari is a huge app with lots of complexity, so if things go wrong it’s hard to tell where to look. I usually create a small test app to use during bring up. The exact function of this test app varies by provider type. For example: If I’m building a packet tunnel provider, I might have a test function that makes an outgoing TCP connection to an IP address. Once I get that working I add another function that makes an outgoing TCP connection to a DNS name. Then I start testing UDP. And so on. Similarly for a content filter, but then it makes sense to add a test that runs a request using URLSession and another one to bring up a WKWebView. If I’m building a DNS proxy provider, my test app might use CFHost to run a simple name-to-address query. Also, consider doing your bring up on the Mac even if your final target is iOS. macOS has a bunch of handy tools for debugging networking issues, including: dig for DNS queries nc for TCP and UDP connections netstat to display the state of the networking stack tcpdump for recording a packet trace [2] Read their respective man pages for all the details. On the other hand, the build / run / debug cycle is simpler on iOS than it is on macOS, especially when you’re building a system extension on macOS. Even if your ultimate goal is to build a macOS-only system extension, if your provider type supports app extension packaging then you should consider whether it makes sense to adopt that packaging just for to speed up your development. If you do decide to try this, be aware that a packaging change can affect your code. See Network Extension Provider Packaging for more on that. [1] The latter is not a profile on macOS, but just a set of instructions. [2] You can use an RVI packet trace on iOS but it’s an extra setup step. Revision History 2026-04-01 Added a suggestion about provider packaging to the General Hints and Tips section. 2023-12-15 Fixed a particularly egregious typo (and spelling error in a section title, no less!). 2023-04-02 Fixed one of the steps in Sytem Extension Hints.
Replies
0
Boosts
0
Views
4.2k
Activity
3h
Network Interface APIs
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" Network Interface APIs Most developers don’t need to interact directly with network interfaces. If you do, read this post for a summary of the APIs available to you. Before you read this, read Network Interface Concepts. Interface List The standard way to get a list of interfaces and their addresses is getifaddrs. To learn more about this API, see its man page. A network interface has four fundamental attributes: A set of flags — These are packed into a CUnsignedInt. The flags bits are declared in <net/if.h>, starting with IFF_UP. An interface type — See Network Interface Type, below. An interface index — Valid indexes are greater than 0. A BSD interface name. For example, an Ethernet interface might be called en0. The interface name is shared between multiple network interfaces running over a given hardware interface. For example, IPv4 and IPv6 running over that Ethernet interface will both have the name en0. WARNING BSD interface names are not considered API. There’s no guarantee, for example, that an iPhone’s Wi-Fi interface is en0. You can map between the last two using if_indextoname and if_nametoindex. See the if_indextoname man page for details. An interface may also have address information. If present, this always includes the interface address (ifa_addr) and the network mask (ifa_netmask). In addition: Broadcast-capable interfaces (IFF_BROADCAST) have a broadcast address (ifa_broadaddr, which is an alias for ifa_dstaddr). Point-to-point interfaces (IFF_POINTOPOINT) have a destination address (ifa_dstaddr). Calling getifaddrs from Swift is a bit tricky. For an example of this, see QSocket: Interfaces. IP Address List Once you have getifaddrs working, it’s relatively easy to manipulate the results to build a list of just IP addresses, a list of IP addresses for each interface, and so on. QSocket: Interfaces has some Swift snippets that show this. Interface List Updates The interface list can change over time. Hardware interfaces can be added and removed, network interfaces come up and go down, and their addresses can change. It’s best to avoid caching information from getifaddrs. If thats unavoidable, use the kNotifySCNetworkChange Darwin notification to update your cache. For information about registering for Darwin notifications, see the notify man page (in section 3). This notification just tells you that something has changed. It’s up to you to fetch the new interface list and adjust your cache accordingly. You’ll find that this notification is sometimes posted numerous times in rapid succession. To avoid unnecessary thrashing, debounce it. While the Darwin notification API is easy to call from Swift, Swift does not import kNotifySCNetworkChange. To fix that, define that value yourself, calling a C function to get the value: var kNotifySCNetworkChange: UnsafePointer<CChar> { networkChangeNotifyKey() } Here’s what that C function looks like: extern const char * networkChangeNotifyKey(void) { return kNotifySCNetworkChange; } Network Interface Type There are two ways to think about a network interface’s type. Historically there were a wide variety of weird and wonderful types of network interfaces. The following code gets this legacy value for a specific BSD interface name: func legacyTypeForInterfaceNamed(_ name: String) -> UInt8? { var addrList: UnsafeMutablePointer<ifaddrs>? = nil let err = getifaddrs(&addrList) // In theory we could check `errno` here but, honestly, what are gonna // do with that info? guard err >= 0, let first = addrList else { return nil } defer { freeifaddrs(addrList) } return sequence(first: first, next: { $0.pointee.ifa_next }) .compactMap { addr in guard let nameC = addr.pointee.ifa_name, name == String(cString: nameC), let sa = addr.pointee.ifa_addr, sa.pointee.sa_family == AF_LINK, let data = addr.pointee.ifa_data else { return nil } return data.assumingMemoryBound(to: if_data.self).pointee.ifi_type } .first } The values are defined in <net/if_types.h>, starting with IFT_OTHER. However, this value is rarely useful because many interfaces ‘look like’ Ethernet and thus have a type of IFT_ETHER. Network framework has the concept of an interface’s functional type. This is an indication of how the interface fits into the system. There are two ways to get an interface’s functional type: If you’re using Network framework and have an NWInterface value, get the type property. If not, call ioctl with a SIOCGIFFUNCTIONALTYPE request. The return values are defined in <net/if.h>, starting with IFRTYPE_FUNCTIONAL_UNKNOWN. Swift does not import SIOCGIFFUNCTIONALTYPE, so it’s best to write this code in a C: extern uint32_t functionalTypeForInterfaceNamed(const char * name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } struct ifreq ifr = {}; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); bool success = ioctl(fd, SIOCGIFFUNCTIONALTYPE, &ifr) >= 0; int junk = close(fd); assert(junk == 0); if ( ! success ) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } return ifr.ifr_ifru.ifru_functional_type; } Finally, TN3158 Resolving Xcode 15 device connection issues documents the SIOCGIFDIRECTLINK flag as a specific way to identify the network interfaces uses by Xcode for device connection traffic. Revision History 2025-12-10 Added info about SIOCGIFDIRECTLINK. 2023-07-19 First posted.
Replies
0
Boosts
0
Views
2.2k
Activity
Dec ’25
Accessory Setup Kit (BLE) not showing multiple options nor the advertising name
I'm developing an application using the accessory setup kit (BLE) on iOS 18+. An important aspect of the connection process is being able to find and choose the correct device. I noticed on iOS 18.2 that I was able to both scroll through the discovered accessories as well as view the advertised name. However, after upgrading to 18.7.2, only a single device is viewable and the advertised name is no longer available. Is there a trigger for this feature that I need to enable or was this "multiple discovery" feature removed? If so, why?
Replies
0
Boosts
1
Views
213
Activity
Oct ’25
Extra-ordinary Networking
Most apps perform ordinary network operations, like fetching an HTTP resource with URLSession and opening a TCP connection to a mail server with Network framework. These operations are not without their challenges, but they’re the well-trodden path. If your app performs ordinary networking, see TN3151 Choosing the right networking API for recommendations as to where to start. Some apps have extra-ordinary networking requirements. For example, apps that: Help the user configure a Wi-Fi accessory Require a connection to run over a specific interface Listen for incoming connections Building such an app is tricky because: Networking is hard in general. Apple devices support very dynamic networking, and your app has to work well in whatever environment it’s running in. Documentation for the APIs you need is tucked away in man pages and doc comments. In many cases you have to assemble these APIs in creative ways. If you’re developing an app with extra-ordinary networking requirements, this post is for you. Note If you have questions or comments about any of the topics discussed here, put them in a new thread here on DevForums. Make sure I see it by putting it in the App & System Services > Networking area. And feel free to add tags appropriate to the specific technology you’re using, like Foundation, CFNetwork, Network, or Network Extension. Links, Links, and More Links Each topic is covered in a separate post: The iOS Wi-Fi Lifecycle describes how iOS joins and leaves Wi-Fi networks. Understanding this is especially important if you’re building an app that works with a Wi-Fi accessory. Network Interface Concepts explains how Apple platforms manage network interfaces. If you’ve got this far, you definitely want to read this. Network Interface Techniques offers a high-level overview of some of the more common techniques you need when working with network interfaces. Network Interface APIs describes APIs and core techniques for working with network interfaces. It’s referenced by many other posts. Running an HTTP Request over WWAN explains why most apps should not force an HTTP request to run over WWAN, what they should do instead, and what to do if you really need that behaviour. If you’re building an iOS app with an embedded network server, see Showing Connection Information in an iOS Server for details on how to get the information to show to your user so they can connect to your server. Many folks run into trouble when they try to find the device’s IP address, or other seemingly simple things, like the name of the Wi-Fi interface. Don’t Try to Get the Device’s IP Address explains why these problems are hard, and offers alternative approaches that function correctly in all network environments. Similarly, folks also run into trouble when trying to get the host name. On Host Names explains why that’s more complex than you might think. If you’re working with broadcasts or multicasts, see Broadcasts and Multicasts, Hints and Tips. If you’re building an app that works with a Wi-Fi accessory, see Working with a Wi-Fi Accessory. If you’re trying to gather network interface statistics, see Network Interface Statistics. There are also some posts that are not part of this series but likely to be of interest if you’re working in this space: TN3179 Understanding local network privacy discusses the local network privacy feature. Calling BSD Sockets from Swift does what it says on the tin, that is, explains how to call BSD Sockets from Swift. When doing weird things with the network, you often find yourself having to use BSD Sockets, and that API is not easy to call from Swift. The code therein is primarily for the benefit of test projects, oh, and DevForums posts like these. TN3111 iOS Wi-Fi API overview is a critical resource if you’re doing Wi-Fi specific stuff on iOS. TLS For Accessory Developers tackles the tricky topic of how to communicate securely with a network-based accessory. A Peek Behind the NECP Curtain discusses NECP, a subsystem that control which programs have access to which network interfaces. Networking Resources has links to many other useful resources. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History 2025-07-31 Added a link to A Peek Behind the NECP Curtain. 2025-03-28 Added a link to On Host Names. 2025-01-16 Added a link to Broadcasts and Multicasts, Hints and Tips. Updated the local network privacy link to point to TN3179. Made other minor editorial changes. 2024-04-30 Added a link to Network Interface Statistics. 2023-09-14 Added a link to TLS For Accessory Developers. 2023-07-23 First posted.
Replies
0
Boosts
0
Views
5.7k
Activity
Jul ’25
Understanding Also-Ran Connections
Every now and again folks notice that Network framework seems to create an unexpected number of connections on the wire. This post explains why that happens and what you should do about it. If you have questions or comments, put them in a new thread here on the forums. Use the App & System Services > Networking topic area and the Network tag. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Understanding Also-Ran Connections Network framework implements the Happy Eyeballs algorithm. That might create more on-the-wire connections than you expect. There are two common places where folks notice this: When looking at a packet trace When implementing a listener Imagine that you’ve implemented a TCP server using NWListener and you connect to it from a client using NWConnection. In many situations there are multiple network paths between the client and the server. For example, on a local network there’s always at least two paths: the link-local IPv6 path and either an infrastructure IPv4 path or the link-local IPv4 path. When you start your NWConnection, Network framework’s Happy Eyeballs algorithm might [1] start a TCP connection for each of these paths. It then races those connections. The one that connects first is the ‘winner’, and Network framework uses that connection for your traffic. Once it has a winner, the other connections, the also-ran connections, are redundant, and Network framework just closes them. You can observe this behaviour on the client side by looking in the system log. Many Network framework log entries (subsystem com.apple.network) contain a connection identifier. For example C8 is the eighth connection started by this process. Each connection may have child connections (C8.1, C8.2, …) and grandchild connections (C8.1.1, C8.1.2, …), and so on. You’ll see state transitions for these child connections occurring in parallel. For example, the following log entries show that C8 is racing the connection of two grandchild connections, C8.1.1 and C8.1.2: type: debug time: 12:22:26.825331+0100 process: TestAlsoRanConnections subsystem: com.apple.network category: connection message: nw_socket_connect [C8.1.1:1] Calling connectx(…) type: debug time: 12:22:26.964150+0100 process: TestAlsoRanConnections subsystem: com.apple.network category: connection message: nw_socket_connect [C8.1.2:1] Calling connectx(…) Note For more information about accessing the system log, see Your Friend the System Log. You also see this on the server side, but in this case each connection is visible to your code. When you connect from the client, Network framework calls your listener’s new connection handler with multiple connections. One of those is the winning connection and you’ll receive traffic on it. The others are the also-ran connections, and they close promptly. IMPORTANT Depending on network conditions there may be no also-ran connections. Or there may be lots of them. If you want to test the also-ran connection case, use Network Link Conditioner to add a bunch of delay to your packets. You don’t need to write special code to handle also-ran connections. From the perspective of your listener, these are simply connections that open and then immediately close. There’s no difference between an also-ran connection and, say, a connection from a client that immediately crashes. Or a connection generated by someone doing a port scan. Your server must be resilient to such things. However, the presence of these also-ran connections can be confusing, especially if you’re just getting started with Network framework, and hence this post. [1] This is “might” because the exact behaviour depends on network conditions. More on that below.
Replies
0
Boosts
0
Views
164
Activity
Apr ’25
Broadcasts and Multicasts, Hints and Tips
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.
Replies
0
Boosts
0
Views
635
Activity
Sep ’25
On FTP
Questions about FTP crop up from time-to-time here on DevForums. In most cases I write a general “don’t use FTP” response, but I don’t have time to go into all the details. I’ve created this post as a place to collect all of those details, so I can reference them in other threads. IMPORTANT Apple’s official position on FTP is: All our FTP APIs have been deprecated, and you should avoid using deprecated APIs. Apple has been slowly removing FTP support from the user-facing parts of our system. The most recent example of this is that we removed the ftp command-line tool in macOS 10.13. You should avoid the FTP protocol and look to adopt more modern alternatives. The rest of this post is an informational explanation of the overall FTP picture. This post is locked so I can keep it focused. If you have questions or comments, please do create a new thread in the App & System Services > Networking subtopic and I’ll respond there. Don’t Use FTP FTP is a very old and very crufty protocol. Certain things that seem obvious to us now — like being able to create a GUI client that reliably shows a directory listing in a platform-independent manner — aren’t possible to do in FTP. However, by far the biggest problem with FTP is that it provides no security [1]. Specifically, the FTP protocol: Provides no on-the-wire privacy, so anyone can see the data you transfer Provides no client-authenticates-server authentication, so you have no idea whether you’re talking to the right server Provides no data integrity, allowing an attacker to munge your data in transit Transfers user names and passwords in the clear Using FTP for anonymous downloads may be acceptable (see the explanation below) but most other uses of FTP are completely inappropriate for the modern Internet. IMPORTANT You should only use FTP for anonymous downloads if you have an independent way to check the integrity of the data you’ve downloaded. For example, if you’re downloading a software update, you could use code signing to check its integrity. If you don’t check the integrity of the data you’ve downloaded, an attacker could substitute a malicious download instead. This would be especially bad in, say, the software update case. These fundamental problems with the FTP protocol mean that it’s not a priority for Apple. This is reflected in the available APIs, which is the subject of the next section. FTP APIs Apple provides two FTP APIs: All Apple platforms provide FTP downloads via URLSession. Most Apple platforms (everything except watchOS) support CFFTPStream, which allows for directory listings, downloads, uploads, and directory creation. All of these FTP APIs are now deprecated: URLSession was deprecated for the purposes of FTP in the 2022 SDKs (macOS 13, iOS 16, iPadOS 16, tvOS 16, watchOS 9) [2]. CFFTPStream was deprecated in the 2016 SDKs (macOS 10.11, iOS 9, iPadOS 9, tvOS 9). CFFTPStream still works about as well as it ever did, which is not particularly well. Specifically: There is at least one known crashing bug (r. 35745763), albeit one that occurs quite infrequently. There are clear implementation limitations — like the fact that CFFTPCreateParsedResourceListing assumes a MacRoman text encoding (r. 7420589) — that won’t be fixed. If you’re looking for an example of how to use these APIs, check out SimpleFTPSample. Note This sample hasn’t been updated since 2013 and is unlikely to ever be updated given Apple’s position on FTP. The FTP support in URLSession has significant limitations: It only supports FTP downloads; there’s no support for uploads or any other FTP operations. It doesn’t support resumable FTP downloads [3]. It doesn’t work in background sessions. That prevents it from running FTP downloads in the background on iOS. It’s only supported in classic loading mode. See the usesClassicLoadingMode property and the doc comments in <Foundation/NSURLSession.h>. If Apple’s FTP APIs are insufficient for your needs, you’ll need to write or acquire your own FTP library. Before you do that, however, consider switching to an alternative protocol. After all, if you’re going to go to the trouble of importing a large FTP library into your code base, you might as well import a library for a better protocol. The next section discusses some options in this space. Alternative Protocols There are numerous better alternatives to FTP: HTTPS is by far the best alternative to FTP, offering good security, good APIs on Apple platforms, good server support, and good network compatibility. Implementing traditional FTP operations over HTTPS can be a bit tricky. One possible way forward is to enable DAV extensions on the server. FTPS is FTP over TLS (aka SSL). While FTPS adds security to the protocol, which is very important, it still inherits many of FTP’s other problems. Personally I try to avoid this protocol. SFTP is a file transfer protocol that’s completely unrelated to FTP. It runs over SSH, making it a great alternative in many of the ad hoc setups that traditionally use FTP. Apple doesn’t have an API for either FTPS or SFTP, although on macOS you may be able to make some headway by invoking the sftp command-line tool. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] In another thread someone asked me about FTP’s other problems, those not related to security, so let’s talk about that. One of FTP’s implicit design goals was to provide cross-platform support that exposes the target platform. You can think of FTP as being kinda like telnet. When you telnet from Unix to VMS, it doesn’t aim to abstract away VMS commands, so that you can type Unix commands at the VMS prompt. Rather, you’re expected to run VMS commands. FTP is (a bit) like that. This choice made sense back when the FTP protocol was invented. Folks were expecting to use FTP via a command-line client, so there was a human in the loop. If they ran a command and it produced VMS-like output, that was fine because they knew that they were FTPing into a VMS machine. However, most users today are using GUI clients, and this design choice makes it very hard to create a general GUI client for FTP. Let’s consider the simple problem of getting the contents of a directory. When you send an FTP LIST command, the server would historically run the platform native directory list command and pipe the results back to you. To create a GUI client you have to parse that data to extract the file names. Doing that is a serious challenge. Indeed, just the first step, working out the text encoding, is a challenge. Many FTP servers use UTF-8, but some use ISO-Latin-1, some use other standard encodings, some use Windows code pages, and so on. I say “historically” above because there have been various efforts to standardise this stuff, both in the RFCs and in individual server implementations. However, if you’re building a general client you can’t rely on these efforts. After all, the reason why folks continue to use FTP is because of it widespread support. [2] To quote the macOS 13 Ventura Release Notes: FTP is deprecated for URLSession and related APIs. Please adopt modern secure networking protocols such as HTTPS. (92623659) [3] Although you can implement resumable downloads using the lower-level CFFTPStream API, courtesy of the kCFStreamPropertyFTPFileTransferOffset property. Revision History 2025-10-06 Explained that URLSession only supports FTP in classic loading mode. Made other minor editorial changes. 2024-04-15 Added a footnote about FTP’s other problems. Made other minor editorial changes. 2022-08-09 Noted that the FTP support in URLSession is now deprecated. Made other minor editorial changes. 2021-04-06 Fixed the formatting. Fixed some links. 2018-02-23 First posted.
Replies
0
Boosts
0
Views
5.8k
Activity
Oct ’25
A Peek Behind the NECP Curtain
From time to time the subject of NECP grows up, both here on DevForums and in DTS cases. I’ve posted about this before but I wanted to collect those tidbits into single coherent post. If you have questions or comments, start a new thread in the App & System Services > Networking subtopic and tag it with Network Extension. That way I’ll be sure to see it go by. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" A Peek Behind the NECP Curtain NECP stands for Network Extension Control Protocol. It’s a subsystem within the Apple networking stack that controls which programs have access to which network interfaces. It’s vitally important to the Network Extension subsystem, hence the name, but it’s used in many different places. Indeed, a very familiar example of its use is the Settings > Mobile Data [1] user interface on iOS. NECP has no explicit API, although there are APIs that are offer some insight into its state. Continuing the Settings > Mobile Data example above, there is a little-known API, CTCellularData in the Core Telephony framework, that returns whether your app has access to WWAN. Despite having no API, NECP is still relevant to developers. The Settings > Mobile Data example is one place where it affects app developers but it’s most important for Network Extension (NE) developers. A key use case for NECP is to prevent VPN loops. When starting an NE provider, the system configures the NECP policy for the NE provider’s process to prevent it from using a VPN interface. This means that you can safely open a network connection inside your VPN provider without having to worry about its traffic being accidentally routed back to you. This is why, for example, an NE packet tunnel provider can use any networking API it wants, including BSD Sockets, to run its connection without fear of creating a VPN loop [1]. One place that NECP shows up regularly is the system log. Next time you see a system log entry like this: type: debug time: 15:02:54.817903+0000 process: Mail subsystem: com.apple.network category: connection message: nw_protocol_socket_set_necp_attributes [C723.1.1:1] setsockopt 39 SO_NECP_ATTRIBUTES … you’ll at least know what the necp means (-: Finally, a lot of NECP infrastructure is in the Darwin open source. As with all things in Darwin, it’s fine to poke around and see how your favourite feature works, but do not incorporate any information you find into your product. Stuff you uncover by looking in Darwin is not considered API. [1] Settings > Cellular Data if you speak American (-: [2] Network Extension providers can call the createTCPConnection(to:enableTLS:tlsParameters:delegate:) method to create an NWTCPConnection [3] that doesn’t run through the tunnel. You can use that if it’s convenient but you don’t need to use it. [3] NWTCPConnection is now deprecated, but there are non-deprecated equivalents. For the full story, see NWEndpoint History and Advice. Revision History 2025-12-12 Replaced “macOS networking stack” with “Apple networking stack” to avoid giving the impression that this is all about macOS. Added a link to NWEndpoint History and Advice. Made other minor editorial changes. 2023-02-27 First posted.
Replies
0
Boosts
0
Views
2.6k
Activity
Dec ’25
My app attempts to use a socket to establish a connection with my external device, but it fails
My external device can generate a fixed Wi-Fi network. When I connect to this Wi-Fi using my iPhone 17 Pro Max (iOS version 26.0.1), and my app tries to establish a connection using the following method, this method returns -1 int connect(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(connect); However, when I use other phones, such as iPhone 12, iPhone 8, iPhone 11, etc., to connect to this external device, the above method always returns successfully, with the parameters passed to the method remaining the same. I also tried resetting the network settings on the iPhone 17 Pro Max (iOS version 26.0.1), but it still cannot establish a connection.
Replies
0
Boosts
0
Views
39
Activity
Oct ’25
iOS Network Signal Strength
This issue has cropped up many times here on DevForums. Someone recently opened a DTS tech support incident about it, and I used that as an opportunity to post a definitive response here. If you have questions or comments about this, start a new thread and tag it with Network so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" iOS Network Signal Strength The iOS SDK has no general-purpose API that returns Wi-Fi or cellular signal strength in real time. Given that this has been the case for more than 10 years, it’s safe to assume that it’s not an accidental omission but a deliberate design choice. For information about the Wi-Fi APIs that are available on iOS, see TN3111 iOS Wi-Fi API overview. Network performance Most folks who ask about this are trying to use the signal strength to estimate network performance. This is a technique that I specifically recommend against. That’s because it produces both false positives and false negatives: The network signal might be weak and yet your app has excellent connectivity. For example, an iOS device on stage at WWDC might have terrible WWAN and Wi-Fi signal but that doesn’t matter because it’s connected to the Ethernet. The network signal might be strong and yet your app has very poor connectivity. For example, if you’re on a train, Wi-Fi signal might be strong in each carriage but the overall connection to the Internet is poor because it’s provided by a single over-stretched WWAN. The only good way to determine whether connectivity is good is to run a network request and see how it performs. If you’re issuing a lot of requests, use the performance of those requests to build a running estimate of how well the network is doing. Indeed, Apple practices what we preach here: This is exactly how HTTP Live Streaming works. Remember that network performance can change from moment to moment. The user’s train might enter or leave a tunnel, the user might step into a lift, and so on. If you build code to estimate the network performance, make sure it reacts to such changes. Keeping all of the above in mind, iOS 26 beta has two new APIs related to this issue: Network framework now offers a linkQuality property. See this post for my take on how to use this effectively. The WirelessInsights framework can notify you of anticipated WWAN condition changes. But what about this code I found on the ’net? Over the years various folks have used various unsupported techniques to get around this limitation. If you find code on the ’net that, say, uses KVC to read undocumented properties, or grovels through system logs, or walks the view hierarchy of the status bar, don’t use it. Such techniques are unsupported and, assuming they haven’t broken yet, are likely to break in the future. But what about Hotspot Helper? Hotspot Helper does have an API to read Wi-Fi signal strength, namely, the signalStrength property. However, this is not a general-purpose API. Like the rest of Hotspot Helper, this is tied to the specific use case for which it was designed. This value only updates in real time for networks that your hotspot helper is managing, as indicated by the isChosenHelper property. But what about MetricKit? MetricKit is so cool. Amongst other things, it supports the MXCellularConditionMetric payload, which holds a summary of the cellular conditions while your app was running. However, this is not a real-time signal strength value. But what if I’m working for a carrier? This post is about APIs in the iOS SDK. If you’re working for a carrier, discuss your requirements with your carrier’s contact at Apple. Revision History 2025-07-02 Updated to cover new features in the iOS 16 beta. Made other minor editorial changes. 2022-12-01 First posted.
Replies
0
Boosts
0
Views
4.6k
Activity
Jul ’25
Network Extension Resources
General: Forums subtopic: App & System Services > Networking DevForums tag: Network Extension Network Extension framework documentation Routing your VPN network traffic article Filtering traffic by URL sample code Filtering Network Traffic sample code TN3120 Expected use cases for Network Extension packet tunnel providers technote TN3134 Network Extension provider deployment technote TN3165 Packet Filter is not API technote Network Extension and VPN Glossary forums post Debugging a Network Extension Provider forums post Exporting a Developer ID Network Extension forums post Network Extension Framework Entitlements forums post Network Extension vs ad hoc techniques on macOS forums post Network Extension Provider Packaging forums post NWEndpoint History and Advice forums post Extra-ordinary Networking forums post Wi-Fi management: Understanding NEHotspotConfigurationErrorInternal forums post See also Networking Resources for general networking resources, including information about Wi-Fi. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
Replies
0
Boosts
0
Views
3.2k
Activity
2w
NSURLSession’s Resume Rate Limiter
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)
Replies
0
Boosts
0
Views
13k
Activity
Jul ’25
Understanding NEHotspotConfigurationErrorInternal
Error 8 in the NEHotspotConfigurationError domain is .internal, aka NEHotspotConfigurationErrorInternal. This error typically indicates that something went wrong in some sort of expected way, but we decided not to surface the exact cause [1]. This has come up a bunch of times before on the forums, and I have various titbits to share. To start, I want to address some specific cases: You’ll see this error if your app isn’t signed with the com.apple.developer.networking.HotspotConfiguration entitlement. To fix this, use Xcode’s Signing & Capabilities editor to add the Hotspot capability to your app. Historically developers reported a situation where once they encountered the error it would show up consistently, but then it would go away on restarting the device. If you see behaviour like that, that’s definitely a bug and I encourage you to file it as such. I have more about filing such bugs in Filing a Wi-Fi Bug Report. Of course, you have to wait to reproduce the error again before you’ll be able to file that bug, because the act of restarting cleared the issue. I’ve seen reports where such problems only occur on a specific type of device, for example, on iPhone 16 but not on earlier or later iPhones. That’s definitely something that Apple should investigate, and I recommend that you file a bug about it. If the problem is being reported by your users but you can’t reproduce it yourself, consider the various suggestions in Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem. Assuming you’re still here (-: the next step is to determine whether the problem is specific to NEHotspotConfigurationManager or not. Try joining the accessory’s network from Settings > Wi-Fi. Does that also have problems? If so, that’s not something we can help you with here on the forums. The focus of the Apple Developer Forums is primarily to help developers with the APIs in Apple’s various platform SDKs [2]. We’re not set up to help accessory developers with Wi-Fi issues. However, there are still things you can do, as I explain in Filing a Wi-Fi Bug Report. At this point you have an error that: Persists across restarts Happens with all Apple devices You can reproduce Only affects NEHotspotConfigurationManager If that’s correct then there are a couple of things you might look at: Coerce the error to an NSError and print that. Does it reveal anything interesting? Also check the underlying error property (NSUnderlyingErrorKey) for hints. When reproducing the error, monitor the system log for log entries in the com.apple.networkextension subsystem. Do those offer any clues? Note For lots of hints and tips about the system log, see Your Friend the System Log. And finally, if you have questions about this case, feel free to start a thread here on the forums and we’ll try to help you out. Put it in the App & System Services > Networking subtopic and tag it with Network Extension. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] There’s also the .unknown error. See this post for a brief summary of the difference. [2] And with Apple tools and some developer-oriented services. Revision History 2026-03-18 Added a missing entitlement bullet to the specific case list. 2026-03-17 First posted.
Replies
0
Boosts
0
Views
60
Activity
2w
About the Relay payload in iOS configuration profiles
Are the network relays introduced in 2023 and https://developer.apple.com/videos/play/wwdc2023/10002/ the same thing as the Private Relay introduced in 2021? https://developer.apple.com/videos/play/wwdc2021/10096/ We are considering verifying the relay function, but we are not sure whether they are the same function or different functions. https://developer.apple.com/documentation/devicemanagement/relay?language=objc
Replies
0
Boosts
0
Views
55
Activity
Apr ’25
CoreBluetooth and BLE AdvertisementData
Hi, We're receiving data via centralManager.centralManager.scanForPeripherals, with no options or filtering (for now), and in the func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) callback, we get advertisementData for each bluetooth device found. But, I know one of my BLE devices is sending an Eddystone TLM payload, which generally is received into the kCBAdvDataServiceData part of the advertisementData dictionary, but, it doesn't show up. What is happening however (when comparing to other devices that do show that payload), is I've noticed the "isConnectable" part is false, and others have it true. Technically we're not "connecting" as such as we're simply reading passive advertisement data, but does that have any bearing on how CoreBluetooth decides to build up it's AdvertisementData response? Example (with serviceData; and I know this has Eddystone TLM) ["kCBAdvDataLocalName": FSC-BP105N, "kCBAdvDataRxPrimaryPHY": 1, "kCBAdvDataServiceUUIDs": <__NSArrayM 0x300b71f80>( FEAA, FEF5 ) , "kCBAdvDataTimestamp": 773270526.26279, "kCBAdvDataServiceData": { FFF0 = {length = 11, bytes = 0x36021892dc0d3015aeb164}; FEAA = {length = 14, bytes = 0x20000be680000339ffa229bbce8a}; }, "kCBAdvDataRxSecondaryPHY": 0, "kCBAdvDataIsConnectable": 1] Vs This also has Eddystone TLM configured ["kCBAdvDataLocalName": 100FA9FD-7000-1000, "kCBAdvDataIsConnectable": 0, "kCBAdvDataRxPrimaryPHY": 1, "kCBAdvDataRxSecondaryPHY": 0, "kCBAdvDataTimestamp": 773270918.97273] Any insight would be great to understand if the presence of other flags drive the exposure of ServiceData or not...
Replies
0
Boosts
0
Views
143
Activity
Jul ’25
iOS 26 Crash: _xzm_xzone_malloc_freelist_outlined in com.apple.network.connections
Hello Apple Support Team, We are seeing a production crash on iOS 26 devices that appears to originate from Apple system frameworks rather than application code. Crash Summary Crash signature: _xzm_xzone_malloc_freelist_outlined Crashed thread: com.apple.network.connections Frameworks involved: CFNetwork, Security, libdispatch, libsystem_malloc Affected OS: iOS 26.x App built with: Xcode 16 Devices: Multiple models (not device-specific) Reproducibility: Intermittent, higher frequency during app launch / background networking Observed Stack Trace (top frames) _xzm_xzone_malloc_freelist_outlined dispatch_data_create_alloc xpc_data_deserialize SecTrustEvaluateIfNecessary CFNetwork HTTPProtocol / HTTP3Connection com.apple.network.connections App Context The app uses URLSession for networking. Multiple third-party SDKs are integrated (Firebase Analytics, Dynatrace, Appsflyer, and similar analytics/monitoring SDKs). These SDKs perform concurrent background network requests, especially during app launch and foreground transitions. No unsafe memory operations (manual malloc/free, unsafe pointers, or custom networking stacks) are used in the app code. Key Observations The crash is predominantly observed on iOS 26 and not on earlier iOS versions. Stack traces do not include application symbols. Disabling or delaying analytics SDK initialization significantly reduces the crash rate. Reducing concurrent network requests and limiting HTTP/3 usage also mitigates the issue. This suggests a potential regression in CFNetwork / Network.framework / HTTP/3 handling combined with the new memory allocator (xzone) on iOS 26. Impact Random app termination during background networking. Occurs without a clear deterministic repro path, making it difficult to fully mitigate at the app level. Request Could you please help investigate whether this is a known iOS 26 issue related to: HTTP/3 / QUIC networking XPC deserialization Memory allocation in the new xzone allocator High-concurrency network requests We would appreciate guidance on: Recommended mitigations Whether this issue is already tracked internally Any best practices for apps integrating multiple analytics SDKs on iOS 26 Crash logs and additional diagnostics can be provided if needed. Thank you for your support. Best regards, Dhananjay
Replies
0
Boosts
1
Views
193
Activity
Feb ’26
Pair iOS Central with MacOS Peripheral for encrypted characteristic
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?
Replies
0
Boosts
0
Views
90
Activity
Feb ’26
TLS for App Developers
Transport Layer Security (TLS) is the most important security protocol on the Internet today. Most notably, TLS puts the S into HTTPS, adding security to the otherwise insecure HTTP protocol. IMPORTANT TLS is the successor to the Secure Sockets Layer (SSL) protocol. SSL is no longer considered secure and it’s now rarely used in practice, although many folks still say SSL when they mean TLS. TLS is a complex protocol. Much of that complexity is hidden from app developers but there are places where it’s important to understand specific details of the protocol in order to meet your requirements. This post explains the fundamentals of TLS, concentrating on the issues that most often confuse app developers. Note The focus of this is TLS-PKI, where PKI stands for public key infrastructure. This is the standard TLS as deployed on the wider Internet. There’s another flavour of TLS, TLS-PSK, where PSK stands for pre-shared key. This has a variety of uses, but an Apple platforms we most commonly see it with local traffic, for example, to talk to a Wi-Fi based accessory. For more on how to use TLS, both TLS-PKI and TLS-PSK, in a local context, see TLS For Accessory Developers. Server Certificates For standard TLS to work the server must have a digital identity, that is, the combination of a certificate and the private key matching the public key embedded in that certificate. TLS Crypto Magic™ ensures that: The client gets a copy of the server’s certificate. The client knows that the server holds the private key matching the public key in that certificate. In a typical TLS handshake the server passes the client a list of certificates, where item 0 is the server’s certificate (the leaf certificate), item N is (optionally) the certificate of the certificate authority that ultimately issued that certificate (the root certificate), and items 1 through N-1 are any intermediate certificates required to build a cryptographic chain of trust from 0 to N. Note The cryptographic chain of trust is established by means of digital signatures. Certificate X in the chain is issued by certificate X+1. The owner of certificate X+1 uses their private key to digitally sign certificate X. The client verifies this signature using the public key embedded in certificate X+1. Eventually this chain terminates in a trusted anchor, that is, a certificate that the client trusts by default. Typically this anchor is a self-signed root certificate from a certificate authority. Note Item N is optional for reasons I’ll explain below. Also, the list of intermediate certificates may be empty (in the case where the root certificate directly issued the leaf certificate) but that’s uncommon for servers in the real world. Once the client gets the server’s certificate, it evaluates trust on that certificate to confirm that it’s talking to the right server. There are three levels of trust evaluation here: Basic X.509 trust evaluation checks that there’s a cryptographic chain of trust from the leaf through the intermediates to a trusted root certificate. The client has a set of trusted root certificates built in (these are from well-known certificate authorities, or CAs), and a site admin can add more via a configuration profile. This step also checks that none of the certificates have expired, and various other more technical criteria (like the Basic Constraints extension). Note This explains why the server does not have to include the root certificate in the list of certificates it passes to the client; the client has to have the root certificate installed if trust evaluation is to succeed. In addition, TLS trust evaluation (per RFC 2818) checks that the DNS name that you connected to matches the DNS name in the certificate. Specifically, the DNS name must be listed in the Subject Alternative Name extension. Note The Subject Alternative Name extension can also contain IP addresses, although that’s a much less well-trodden path. Also, historically it was common to accept DNS names in the Common Name element of the Subject but that is no longer the case on Apple platforms. App Transport Security (ATS) adds its own security checks. Basic X.509 and TLS trust evaluation are done for all TLS connections. ATS is only done on TLS connections made by URLSession and things layered on top URLSession (like WKWebView). In many situations you can override trust evaluation; for details, see Technote 2232 HTTPS Server Trust Evaluation). Such overrides can either tighten or loosen security. For example: You might tighten security by checking that the server certificate was issued by a specific CA. That way, if someone manages to convince a poorly-managed CA to issue them a certificate for your server, you can detect that and fail. You might loosen security by adding your own CA’s root certificate as a trusted anchor. IMPORTANT If you rely on loosened security you have to disable ATS. If you leave ATS enabled, it requires that the default server trust evaluation succeeds regardless of any customisations you do. Mutual TLS The previous section discusses server trust evaluation, which is required for all standard TLS connections. That process describes how the client decides whether to trust the server. Mutual TLS (mTLS) is the opposite of that, that is, it’s the process by which the server decides whether to trust the client. Note mTLS is commonly called client certificate authentication. I avoid that term because of the ongoing industry-wide confusion between certificates and digital identities. While it’s true that, in mTLS, the server authenticates the client certificate, to set this up on the client you need a digital identity, not a certificate. mTLS authentication is optional. The server must request a certificate from the client and the client may choose to supply one or not (although if the server requests a certificate and the client doesn’t supply one it’s likely that the server will then fail the connection). At the TLS protocol level this works much like it does with the server certificate. For the client to provide this certificate it must apply a digital identity, known as the client identity, to the connection. TLS Crypto Magic™ assures the server that, if it gets a certificate from the client, the client holds the private key associated with that certificate. Where things diverge is in trust evaluation. Trust evaluation of the client certificate is done on the server, and the server uses its own rules to decided whether to trust a specific client certificate. For example: Some servers do basic X.509 trust evaluation and then check that the chain of trust leads to one specific root certificate; that is, a client is trusted if it holds a digital identity whose certificate was issued by a specific CA. Some servers just check the certificate against a list of known trusted client certificates. When the client sends its certificate to the server it actually sends a list of certificates, much as I’ve described above for the server’s certificates. In many cases the client only needs to send item 0, that is, its leaf certificate. That’s because: The server already has the intermediate certificates required to build a chain of trust from that leaf to its root. There’s no point sending the root, as I discussed above in the context of server trust evaluation. However, there are no hard and fast rules here; the server does its client trust evaluation using its own internal logic, and it’s possible that this logic might require the client to present intermediates, or indeed present the root certificate even though it’s typically redundant. If you have problems with this, you’ll have to ask the folks running the server to explain its requirements. Note If you need to send additional certificates to the server, pass them to the certificates parameter of the method you use to create your URLCredential (typically init(identity:certificates:persistence:)). One thing that bears repeating is that trust evaluation of the client certificate is done on the server, not the client. The client doesn’t care whether the client certificate is trusted or not. Rather, it simply passes that certificate the server and it’s up to the server to make that decision. When a server requests a certificate from the client, it may supply a list of acceptable certificate authorities [1]. Safari uses this to filter the list of client identities it presents to the user. If you are building an HTTPS server and find that Safari doesn’t show the expected client identity, make sure you have this configured correctly. If you’re building an iOS app and want to implement a filter like Safari’s, get this list using: The distinguishedNames property, if you’re using URLSession The sec_protocol_metadata_access_distinguished_names routine, if you’re using Network framework [1] See the certificate_authorities field in Section 7.4.4 of RFC 5246, and equivalent features in other TLS versions. Self-Signed Certificates Self-signed certificates are an ongoing source of problems with TLS. There’s only one unequivocally correct place to use a self-signed certificate: the trusted anchor provided by a certificate authority. One place where a self-signed certificate might make sense is in a local environment, that is, securing a connection between peers without any centralised infrastructure. However, depending on the specific circumstances there may be a better option. TLS For Accessory Developers discusses this topic in detail. Finally, it’s common for folks to use self-signed certificates for testing. I’m not a fan of that approach. Rather, I recommend the approach described in QA1948 HTTPS and Test Servers. For advice on how to set that up using just your Mac, see TN2326 Creating Certificates for TLS Testing. TLS Standards RFC 6101 The Secure Sockets Layer (SSL) Protocol Version 3.0 (historic) RFC 2246 The TLS Protocol Version 1.0 RFC 4346 The Transport Layer Security (TLS) Protocol Version 1.1 RFC 5246 The Transport Layer Security (TLS) Protocol Version 1.2 RFC 8446 The Transport Layer Security (TLS) Protocol Version 1.3 RFC 4347 Datagram Transport Layer Security RFC 6347 Datagram Transport Layer Security Version 1.2 RFC 9147 The Datagram Transport Layer Security (DTLS) Protocol Version 1.3 Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History: 2025-11-21 Clearly defined the terms TLS-PKI and TLS-PSK. 2024-03-19 Adopted the term mutual TLS in preference to client certificate authentication throughout, because the latter feeds into the ongoing certificate versus digital identity confusion. Defined the term client identity. Added the Self-Signed Certificates section. Made other minor editorial changes. 2023-02-28 Added an explanation mTLS acceptable certificate authorities. 2022-12-02 Added links to the DTLS RFCs. 2022-08-24 Added links to the TLS RFCs. Made other minor editorial changes. 2022-06-03 Added a link to TLS For Accessory Developers. 2021-02-26 Fixed the formatting. Clarified that ATS only applies to URLSession. Minor editorial changes. 2020-04-17 Updated the discussion of Subject Alternative Name to account for changes in the 2019 OS releases. Minor editorial updates. 2018-10-29 Minor editorial updates. 2016-11-11 First posted.
Replies
0
Boosts
0
Views
8.3k
Activity
Nov ’25
NWEndpoint History and Advice
The path from Network Extension’s in-provider networking APIs to Network framework has been long and somewhat rocky. The most common cause of confusion is NWEndpoint, where the same name can refer to two completely different types. I’ve helped a bunch of folks with this over the years, and I’ve decided to create this post to collect together all of those titbits. If you have questions or comments, please put them in a new thread. Put it in the App & System Services > Networking subtopic and tag it with Network Extension. That way I’ll be sure to see it go by. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" NWEndpoint History and Advice A tale that spans three APIs, two languages, and ten years. The NWEndpoint type has a long and complex history, and if you’re not aware of that history you can bump into weird problems. The goal of this post is to explain the history and then offer advice on how to get around specific problems. IMPORTANT This post focuses on NWEndpoint, because that’s the type that causes the most problems, but there’s a similar situation with NWPath. The History In iOS 9 Apple introduced the Network Extension (NE) framework, which offers a convenient way for developers to create a custom VPN transport. Network Extension types all have the NE prefix. Note I’m gonna use iOS versions here, just to keep the text simple. If you’re targeting some other platform, use this handy conversion table: iOS | macOS | tvOS | watchOS | visionOS --- + ----- + ---- + ------- + -------- 9 | 10.11 | 9 | 2 | - 12 | 10.14 | 12 | 5 | - 18 | 15 | 18 | 11 | 2 At that time we also introduced in-provider networking APIs. The idea was that an NE provider could uses these Objective-C APIs to communicate with its VPN server, and thereby avoiding a bunch of ugly BSD Sockets code. The in-provider networking APIs were limited to NE providers. Specifically, the APIs to construct an in-provider connection were placed on types that were only usable within an NE provider. For example, a packet tunnel provider could create a NWTCPConnection object by calling -createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate:] and -createTCPConnectionThroughTunnelToEndpoint:enableTLS:TLSParameters:delegate:, which are both methods on NEPacketTunnelProvider. These in-provider networking APIs came with a number of ancillary types, including NWEndpoint and NWPath. At the time we thought that we might promote these in-provider networking APIs to general-purpose networking APIs. That’s why the APIs use the NW prefix. For example, it’s NWTCPConnection, not NETCPConnection. However, plans changed. In iOS 12 Apple shipped Network framework as our recommended general-purpose networking API. This actually includes two APIs: A Swift API that follows Swift conventions, for example, the connection type is called NWConnection A C API that follows C conventions, for example, the connection type is called nw_connection_t These APIs follow similar design patterns to the in-provider networking API, and thus have similar ancillary types. Specifically, there are an NWEndpoint and nw_endpoint_t types, both of which perform a similar role to the NWEndpoint type in the in-provider networking API. This was a source of some confusion in Swift, because the name NWEndpoint could refer to either the Network framework type or the Network Extension framework type, depending on what you’d included. Fortunately you could get around this by qualifying the type as either Network.NWEndpoint or NetworkExtension.NWEndpoint. The arrival of Network framework meant that it no longer made sense to promote the in-provider networking APIs to general-purposes networking APIs. The in-provider networking APIs were on the path to deprecation. However, deprecating these APIs was actually quite tricky. Network Extension framework uses these APIs in a number of interesting ways, and so deprecating them required adding replacements. In addition, we’d needed different replacements for Swift and Objective-C, because Network framework has separate APIs for Swift and C-based languages. In iOS 18 we tackled that problem head on. To continue the NWTCPConnection example above, we replaced: -createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate:] with nw_connection_t -createTCPConnectionThroughTunnelToEndpoint:enableTLS:TLSParameters:delegate: with nw_connection_t combined with a new virtualInterface property on NEPacketTunnelProvider Of course that’s the Objective-C side of things. In Swift, the replacement is NWConnection rather than nw_connection_t, and the type of the virtualInterface property is NWInterface rather than nw_interface_t. But that’s not the full story. For the two types that use the same name in both frameworks, NWEndpoint and NWPath, we decided to use this opportunity to sort out that confusion. To see how we did that, check out the <NetworkExtension/NetworkExtension.apinotes> file in the SDK. Focusing on NWEndpoint for the moment, you’ll find two entries: … - Name: NWEndpoint SwiftPrivate: true … SwiftVersions: - Version: 5.0 … - Name: NWEndpoint SwiftPrivate: false … The first entry applies when you’re building with the Swift 6 language mode. This marks the type as SwiftPrivate, which means that Swift imports it as __NWEndpoint. That frees up the NWEndpoint name to refer exclusively to the Network framework type. The second entry applies when you’re building with the Swift 5 language mode. It marks the type as not SwiftPrivate. This is a compatible measure to ensure that code written for Swift 5 continues to build. The Advice This sections discusses specific cases in this transition. NWEndpoint and NWPath In Swift 5 language mode, NWEndpoint and NWPath might refer to either framework, depending on what you’ve imported. Add a qualifier if there’s any ambiguity, for example, Network.NWEndpoint or NetworkExtension.NWEndpoint. In Swift 6 language mode, NWEndpoint and NWPath always refer to the Network framework type. Add a __ prefix to get to the Network Extension type. For example, use NWEndpoint for the Network framework type and __NWEndpoint for the Network Extension type. Direct and Through-Tunnel TCP Connections in Swift To create a connection directly, simply create an NWConnection. This support both TCP and UDP, with or without TLS. To create a connection through the tunnel, replace code like this: let c = self.createTCPConnectionThroughTunnel(…) with code like this: let params = NWParameters.tcp params.requiredInterface = self.virtualInterface let c = NWConnection(to: …, using: params) This is for TCP but the same basic process applies to UDP. UDP and App Proxies in Swift If you’re building an app proxy, transparent proxy, or DNS proxy in Swift and need to handle UDP flows using the new API, adopt the NEAppProxyUDPFlowHandling protocol. So, replace code like this: class AppProxyProvider: NEAppProxyProvider { … override func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteEndpoint remoteEndpoint: NWEndpoint) -> Bool { … } } with this: class AppProxyProvider: NEAppProxyProvider, NEAppProxyUDPFlowHandling { … func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteFlowEndpoint remoteEndpoint: NWEndpoint) -> Bool { … } } Creating a Network Rule To create an NWHostEndpoint, replace code like this: let ep = NWHostEndpoint(hostname: "1.2.3.4", port: "12345") let r = NENetworkRule(destinationHost: ep, protocol: .TCP) with this: let ep = NWEndpoint.hostPort(host: "1.2.3.4", port: 12345) let r = NENetworkRule(destinationHostEndpoint: ep, protocol: .TCP) Note how the first label of the initialiser has changed from destinationHost to destinationHostEndpoint.
Replies
0
Boosts
0
Views
270
Activity
Jul ’25
Network Extension Framework Entitlements
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.
Replies
0
Boosts
0
Views
23k
Activity
Nov ’25