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

AdHoc IPA: "permission denied" when installing DNS Proxy NetworkExtension (all entitlements and profiles seem correct)
Hi all! I’m having trouble distributing an iOS app with a DNS Proxy NetworkExtension via AdHoc. The app and extension work perfectly with development profiles, but when I export and install the AdHoc IPA, I get a “permission denied” error when trying to install/enable the DNS Proxy extension. What I’ve done: Both the app and the DNS Proxy extension have their own App IDs in the Apple Developer portal. Both App IDs have the same App Group enabled: group.com.irakai.SafeLinkApp2. The extension App ID has the NetworkExtension capability with dns-proxy enabled. I created two AdHoc provisioning profiles (one for the app, one for the extension), both including the same devices and the correct entitlements. I assigned the correct AdHoc profiles to each target in Xcode and exported the IPA via Organizer. I install the IPA on a registered device using Apple Configurator. Entitlements (extracted from the signed binaries on device): App: <key>application-identifier</key><string>6PBG234246.com.irakai.SafeLinkApp2</string> <key>com.apple.developer.networking.networkextension</key><array> <string>packet-tunnel-provider</string> <string>dns-proxy</string> </array> <key>com.apple.developer.team-identifier</key><string>6PBG234246</string> <key>com.apple.security.application-groups</key><array> <string>group.com.irakai.SafeLinkApp2</string> </array> <key>get-task-allow</key><false/> DNSProxy Extension: <key>application-identifier</key><string>6PBG234246.com.irakai.SafeLinkApp2.DNSProxy</string> <key>com.apple.developer.networking.networkextension</key><array> <string>dns-proxy</string> </array> <key>com.apple.developer.team-identifier</key><string>6PBG234246</string> <key>com.apple.security.application-groups</key><array> <string>group.com.irakai.SafeLinkApp2</string> </array> <key>get-task-allow</key><false/> Error message (from my app’s logs): Error instalando DNS Proxy: permission denied Usuario: Roberto AppGroup: group.com.irakai.SafeLinkApp2 AppGroupPath: /private/var/mobile/Containers/Shared/AppGroup/D8AD2DED-AD96-4915-9B7A-648C9504679B Entitlements: BundleId: com.irakai.SafeLinkApp2 Debug info: Error Domain=NEDNSProxyErrorDomain Code=1 "permission denied" UserInfo={NSLocalizedDescription=permission denied} Other details: The device is included in both AdHoc profiles. The App Group is present and identical in both entitlements. The extension’s bundle identifier matches the App ID in the portal. The extension is signed with the correct AdHoc profile. I have tried rebooting the device and reinstalling the IPA. The error only occurs with AdHoc; development builds work fine. Questions: Is there anything else I should check regarding AdHoc provisioning for NetworkExtension DNS Proxy? Are there any known issues with AdHoc and NetworkExtension on recent iOS versions? Is there a way to get more detailed diagnostics from the system about why the permission is denied? Could this be a bug in iOS, or am I missing a subtle configuration step? Any help or suggestions would be greatly appreciated. Thank you!
1
0
136
Jun ’25
Content Filters on devices without family controls authorisation.
I’m working on an iOS parental-control app that needs to block specific network traffic (e.g. certain domains or URLs). We’ve already obtained the Family Controls entitlement (since our app is explicitly a parental-control solution), but we do not use MDM to supervise devices. In testing, our NEFilterDataProvider extension only activates when the device is enrolled under a managed Family Controls profile. I am aware that we can use a PacketTunnel to achieve this but i was wondering if there is any simpler solution to this? Thanks for you time!
4
0
221
Jun ’25
Ping without CFSockets
All of our uses of CFSockets have started causing crashes in iOS 16. They seem to be deprecated so we are trying to transition over to using the Network framework and NWConnection to try to fix the crashes. One of our uses of them is to ping a device on the local network to make sure it is there and online and provide a heartbeat status in logs as well as put the application into a disabled state if it is not available as it is critical to the functionality of the app. I know it is discouraged to disable any functionality based on the reachability of a resource but this is in an enterprise environment where the reachability of this device is mission critical. I've seen other people ask about the ability to ping with the Network framework and the answers I've found have said that this is not possible and pointed people to the SimplePing sample code but it turns out our existing ping code is already using this technique and it is crashing just like our other CFSocket usages, inside CFSocketInvalidate with the error BUG IN CLIENT OF LIBPLATFORM: Trying to recursively lock an os_unfair_lock. Is there any updated way to perform a ping without using the CFSocket APIs that now seem to be broken/unsupported on iOS 16?
7
0
2.1k
Mar ’26
Enhancing NEFilterPacketProvider with Process-Level Filtering
Our application currently uses NEFilterPacketProvider to filter network traffic based on Layer 4 rules (5-tuple: source IP, destination IP, source port, destination port, and protocol) on a packet-by-packet basis. We now want to extend this filtering to also consider the associated process—for example, allowing traffic from a specific source IP to a destination IP and port only if it's associated with a specific local process. That is, we’d like to make filtering decisions not just based on the 5-tuple, but also on the identity of the process either sending or receiving the traffic. We’ve looked into NEFilterSocketProvider, which does expose Layer 7 information such as process identifiers. However, it doesn’t seem to be tightly synchronized with the packet flow handled by NEFilterPacketProvider. As a result, there’s a risk that we might only get process information after the TCP handshake is complete, or before the socket is fully bound—at which point some of the 5-tuple fields (such as the local port) may still be unavailable. What we need is a way to correlate the 5-tuple with the relevant process name (either sender or receiver) at the time the first packet—e.g., a SYN packet—is about to be sent or received. Is there a recommended way to achieve this kind of early, process-aware filtering using NetworkExtension APIs?
1
0
106
Jun ’25
wifi connect fail
Dear Apple: In our app, we will call the - (void) applyConfiguration:(NEHotspotConfiguration *) configuration completionHandler:(void (^)(NSError * error)) completionHandler; interface of NEHotspotConfigurationManager on Apple devices. However, we are encountering a problem where the connection to the 2.4G hotspot fails, and the error is nil when it fails. We checked the Wi-Fi air interface and found that the Apple phone does not send a probe request before connecting to the hotspot. However, we are unclear why the Apple device does not send the probe request frame. Could you please help us understand when the probe request frame is not sent during the hotspot connection and how to trigger it to send the probe request frame every time? Thank you.
4
0
149
May ’25
IOS app on MacOS 15 local network access
Our app is developed for iOS, but some users also run it on macOS (as an iOS app via Apple Silicon). The app requires local network permission, which works perfectly on iOS. Previously, the connection also worked fine on macOS, but since the recent macOS update, the app can no longer connect to our device. Additionally, our app on macOS doesn't prompt for local network permission at all, whereas it does on iOS. Is this a known issue with iOS apps running on macOS? Has anyone else experienced this problem, or is there a workaround? Any help would be appreciated!
9
0
951
Oct ’25
Split tunnel w/o changing route table
I've built a VPN app that is based on wireguard on macOS (I have both AppStore ver. and Developer ID ver). I want to achieve split tunneling function without changing the system route table. Currently, I'm making changes in PacketTunnelProvider: NEPacketTunnelProvider. It has included/excluded routes that function as a split tunnel, just that all changes are immediately reflected on the route table: if I run netstat -rn in terminal, I would see all rules/CIDRs I added, displayed all at once. Since I have a CIDR list of ~800 entries, I'd like to avoid changing the route table directly. I've asked ChatGPT, Claude, DeepSeek, .etc. An idea was to implement an 'interceptor' to intercept all packets in packetFlow(_:readPacketsWithCompletionHandler:), extract the destination IP from each packet, check if it matches your CIDR list, and either reinject it back to the system interface (for local routing) or process it through your tunnel. Well, LLMs could have hallucinations and I've pretty new to macOS programming. I'm asking to make sure I'm on the right track, not going delusional with those LLMs :) So the question is, does the above method sounds feasible? If not, is it possible to achieve split tunneling without changing the route table?
4
0
136
Jun ’25
URLSessionWebSocketTask reports closeCode as invalid when state is completed
I am using a URLSessionWebSocketTask. When trying to receive messages while the app is backgrounded, the receive() method fails with an NSError where the domain is NSPOSIXErrorDomain and the code is ECONNABORTED. That behavior is good. However, when this happens, the URLSessionWebSocketTask reports a closeCode of invalid, which is supposed to denote that the connection is still open. However, the connection state property is reporting completed. I feel that the closeCode property should be reporting something like abnormalClosure in this case. Either way, this seems like a bug or the documentation is incorrect.
2
3
220
Apr ’25
iPhone App 进行网络请求的时候出错,报错信息为:未能完成该操作。设备上无剩余空间
1、已经检查过手机的存储空间,还有一百多G的空间。app端进行网络接口情况的时候报错了,报错信息如下: Error : Error Domain=NSPOSIXErrorDomain Code=28 "No space left on device" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <7DB1CBFD-B9BE-422D-9C9A-78D8FC04DC1B>.<76>, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=28, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <7DB1CBFD-B9BE-422D-9C9A-78D8FC04DC1B>.<76>" ), _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: pdp_ip0[lte], ipv4, ipv6, dns, expensive, estimated upload: 65536Bps, uses cell} 2、手机型号是iPhone 15 Plus,iOS 17.6.1
2
0
419
Jun ’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
Real-time audio application on locked device
I would like to inquire about the feasibility of developing an iOS application with the following requirements: The app must support real-time audio communication based on UDP. It needs to maintain a TCP signaling connection, even when the device is locked. The app will run only on selected devices within a controlled (closed) environment, such as company-managed iPads or iPhones. Could you please clarify the following: Is it technically possible to maintain an active TCP connection when the device is locked? What are the current iOS restrictions or limitations for background execution, particularly related to networking and audio? Are there any recommended APIs or frameworks (such as VoIP, PushKit, or Background Modes) suitable for this type of application?
1
0
196
Jun ’25
Can NWBrowser be used in an AppIntent extension? NoAuth
I have an iOS app that connects to a server running on macOS by leveraging NWListener & NWBrowser. It also makes use of the peerToPeer functionality / AWDL offered via the Network framework. This works great in the iOS app. Now I would like to add support for Shortcuts / App Intents in general. The NWConnection on its own is also working great in the App Intent, but only if I provide the host/port manually, which means I can't use the peer to peer functionality. If I try to run my NWBrowser in the AppIntent it immediately changes its state to failed with a NoAuth (-65555) error: nw_browser_cancel [B1517] The browser has already been cancelled, ignoring nw_browser_cancel(). nw_browser_fail_on_dns_error_locked [B1518] DNSServiceBrowse failed: NoAuth(-65555) NWClientManager: Browser failed: -65555: NoAuth I haven't found documentation/information on whether NWBrowser should work in an AppIntent extension or not.
1
0
129
Apr ’25
Entitlement Request Support
We require the following Network Extension entitlements without the -systemextension suffix: packet-tunnel-provider app-proxy-provider Our application uses the legacy NetworkExtension framework, not the newer System Extensions. Although our provisioning profile has been approved by Apple, the entitlements are still being suffixed automatically with -systemextension. Since our code is built on the legacy NetworkExtension framework, this causes VPN functionality to break. Target platforms: macOS 14 & 15 (distributed outside the Mac App Store via a .pkg installer). Is there a way to use the original (non-systemextension) entitlements in this setup?
3
0
278
Jun ’25
Content Filter: sourceAppAuditToken empty only for Firefox
Starting on macOS Sequoia, flows originated in Firefox have an empty sourceAppAuditToken. Other apps contain a valid token. Background: in order to fetch process info for a certain NEFilterFlow, my content filter extension uses sourceAppAuditToken, audit_token_to_pid() and proc_* (as recommended in #126820). When that fails, we use SecCodeCopyGuestWithAttributes, recommended in some other thread as a better alternative. Both approaches break when the sourceAppAuditToken is empty since they need the pid. Debugging: My logging shows audit token is empty for Firefox Typical logs from com.apple.networkextension also indicate it fails to fetch the same info I'm looking for: com.apple.networkextension debug 11:22:07.024588-0300 Fetching appInfo from cache for pid: 948 uuid: 5C40B765-C6C9-3641-A822-2BC44D264361 bundle id: (null) com.apple.networkextension debug 11:22:07.024657-0300 Calling delegate lookup handler with pid: 948, uuid: 5C40B765-C6C9-3641-A822-2BC44D264361, bundleID: (null) com.apple.networkextension debug 11:22:07.025856-0300 Could not look up appInfo for pid: 948 bundle id: (null) uuid: 5C40B765-C6C9-3641-A822-2BC44D264361 com.apple.networkextension error 11:22:07.025897-0300 Could not find app info, return the original flow without filling in app info Handling new flow: identifier = D89B5B5D-793C-4940-D992-4E90F2AD1900 procPID = 953 eprocPID = 948 direction = outbound inBytes = 0 outBytes = 0 signature = {length = 32, bytes = 0x4afeafde b484aa0c c5cb8698 0567343d ... 7cdee33e 135666dd } socketID = 19adf2904e92d9 localEndpoint = 0.0.0.0:0 remoteEndpoint = 17.33.202.170:443 protocol = 6 family = 2 type = 1 procUUID = 0C68E603-967E-3643-B225-378BD2A655F7 eprocUUID = 5C40B765-C6C9-3641-A822-2BC44D264361 Perhaps there's a bug when generating the audit token or could it be something with the Firefox signature? I double-checked Firefox and it seems fine: $ codesign --verify --verbose /Applications/Firefox.app /Applications/Firefox.app: valid on disk /Applications/Firefox.app: satisfies its Designated Requirement Not sure if relevant, but codesign with -dv showed different flags in CodeDirectory when compared to chrome: codesign -dv /Applications/Firefox.app ... CodeDirectory v=20500 size=863 flags=0x10000(runtime) hashes=18+5 ... Versus chrome CodeDirectory v=20500 size=1821 flags=0x12a00(kill,restrict,library-validation,runtime) hashes=46+7 location=embedded
3
0
570
Aug ’25
Can't find server for API Endpoint that works.
Hi, I am making a AI-Powered app that makes api requests to the openai API. However, for security, I set up a vercel backend that handles the API calls securely, while my frontend makes a call to my vercel-hosted https endpoint. Interestingly, whenever I try to make that call on my device, an iPhone, I get this error: Task <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10> finished with error [-1003] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={_kCFStreamErrorCodeKey=-72000, NSUnderlyingError=0x1435783f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorDomainKey=10, _kCFStreamErrorCodeKey=-72000, _NSURLErrorNWResolutionReportKey=Resolved 0 endpoints in 3ms using unknown from query, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: pdp_ip0[lte], ipv4, ipv6, dns, expensive, uses cell}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>" ), NSLocalizedDescription=A server with the specified hostname could not be found., NSErrorFailingURLStringKey=https://[my endpoint], NSErrorFailingURLKey=https://[my endpoint], _kCFStreamErrorDomainKey=10} I'm completely stuck because when I directly make https requests to other api's like openai's endpoint, without the proxy, it finds the server completely fine. Running my endpoint on terminal with curl also works as intended, as I see api key usages. But for some reason, on my project, it does not work. I've looked through almost every single post I could find online, but a lot all of the solutions are outdated and unhelpful. I'm willing to schedule a call, meeting, whatever to resolve this issue and get help more in depth as well.
1
0
163
Jun ’25
iOS not launching my app network extension, it seemingly isn't crashing it either
My personal project is a bit further along however after not being able to get this to work in my app I fell back to a much simpler/proven implementation out there. There is this project on GitHub with a guide that implements a barebones app extension with packet tunneling. I figure this can give us common ground. After changing the bundle and group identifiers to all end with -Caleb and or match up I tried running the app. The app extension does not work whatsoever and seemingly for reasons that are similar to my personal project. If I pull up the console and filter for the subsystem (com.github.kean.vpn-client-caleb.vpn-tunnel) I see the following. First you see installd installing it 0x16ba5f000 -[MIUninstaller _uninstallBundleWithIdentity:linkedToChildren:waitForDeletion:uninstallReason:temporaryReference:deleteDataContainers:wasLastReference:error:]: Destroying container com.github.kean.vpn-client-caleb.vpn-tunnel with persona 54D15361-A614-4E0D-931A-0953CDB50CE8 at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65 And then installd says this (No idea what it means) 0x16b9d3000 -[MIInstallationJournalEntry _refreshUUIDForContainer:withError:]: Data container for com.github.kean.vpn-client-caleb.vpn-tunnel is now at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65 Concerningly runningboardd seems to immediately try and stop it? Executing termination request for: <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> [app<com.github.kean.vpn-client-caleb(54D15361-A614-4E0D-931A-0953CDB50CE8)>:1054] Terminating with context: <RBSTerminateContext| explanation:installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y reportType:None maxTerminationResistance:Absolute attrs:[ <RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> allow:(null)> ]> Then runningboardd leaves a cryptic message Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd>:244] with description <RBSAssertionDescriptor| "installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y" ID:33-244-5222 target:system attributes:[ <RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> allow:(null)> ]> And that seems to be all I have to go off of.... If I widen my search a bit I can see backboardd saying things like Connection removed: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{ HighFrequency = 1; bundleID = "com.github.kean.vpn-client-caleb"; pid = 1054; } state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE Or Removing client connection <BKHIDClientConnection: 0xbf9828cd0; IOHIDEventSystemConnectionRef: 0xbf96d9600; vpid: 1054(vAF7); taskPort: 0x5D777; bundleID: com.github.kean.vpn-client-caleb> for client: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{ HighFrequency = 1; bundleID = "com.github.kean.vpn-client-caleb"; pid = 1054; } state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE source:HID There's really nothing in the sysdiagnose either. No crash no nothing. I am stumped. Any idea what might be going wrong for me here? Has something about the way app extensions or sandbox rules work changed in later OSes?
1
0
225
Apr ’25
Optimization Suggestion: Update Queue Prioritization by Payload Size.
Dear Developers, I would like to suggest an optimization for the logic governing the download and installation queue for app updates. Currently, when multiple applications are awaiting updates, the prioritization does not appear to consider the update payload size. My proposal is to implement a logic that prioritizes the download and installation of updates with a smaller delta size (fewer MB) before those with a larger delta. Practical Example: A 1MB update would be processed before a 500MB update, even if their arrival order in the queue was inverted. Potential Benefits: Perceived Speed Optimization (UX): Users would gain access to functional applications more quickly, especially in scenarios with multiple pending updates. Network Efficiency: In limited or intermittent bandwidth scenarios, completing smaller downloads first can reduce the chance of download failures and optimize network resource utilization. Device Resource Management: Frees up temporary storage and processing resources more rapidly for smaller updates. I believe this optimization would bring significant gains in terms of User Experience (UX) and the operational efficiency of the platform. Thank you for your attention and consideration. Sincerely,
1
0
166
Jun ’25
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.3k
1w
AdHoc IPA: "permission denied" when installing DNS Proxy NetworkExtension (all entitlements and profiles seem correct)
Hi all! I’m having trouble distributing an iOS app with a DNS Proxy NetworkExtension via AdHoc. The app and extension work perfectly with development profiles, but when I export and install the AdHoc IPA, I get a “permission denied” error when trying to install/enable the DNS Proxy extension. What I’ve done: Both the app and the DNS Proxy extension have their own App IDs in the Apple Developer portal. Both App IDs have the same App Group enabled: group.com.irakai.SafeLinkApp2. The extension App ID has the NetworkExtension capability with dns-proxy enabled. I created two AdHoc provisioning profiles (one for the app, one for the extension), both including the same devices and the correct entitlements. I assigned the correct AdHoc profiles to each target in Xcode and exported the IPA via Organizer. I install the IPA on a registered device using Apple Configurator. Entitlements (extracted from the signed binaries on device): App: <key>application-identifier</key><string>6PBG234246.com.irakai.SafeLinkApp2</string> <key>com.apple.developer.networking.networkextension</key><array> <string>packet-tunnel-provider</string> <string>dns-proxy</string> </array> <key>com.apple.developer.team-identifier</key><string>6PBG234246</string> <key>com.apple.security.application-groups</key><array> <string>group.com.irakai.SafeLinkApp2</string> </array> <key>get-task-allow</key><false/> DNSProxy Extension: <key>application-identifier</key><string>6PBG234246.com.irakai.SafeLinkApp2.DNSProxy</string> <key>com.apple.developer.networking.networkextension</key><array> <string>dns-proxy</string> </array> <key>com.apple.developer.team-identifier</key><string>6PBG234246</string> <key>com.apple.security.application-groups</key><array> <string>group.com.irakai.SafeLinkApp2</string> </array> <key>get-task-allow</key><false/> Error message (from my app’s logs): Error instalando DNS Proxy: permission denied Usuario: Roberto AppGroup: group.com.irakai.SafeLinkApp2 AppGroupPath: /private/var/mobile/Containers/Shared/AppGroup/D8AD2DED-AD96-4915-9B7A-648C9504679B Entitlements: BundleId: com.irakai.SafeLinkApp2 Debug info: Error Domain=NEDNSProxyErrorDomain Code=1 "permission denied" UserInfo={NSLocalizedDescription=permission denied} Other details: The device is included in both AdHoc profiles. The App Group is present and identical in both entitlements. The extension’s bundle identifier matches the App ID in the portal. The extension is signed with the correct AdHoc profile. I have tried rebooting the device and reinstalling the IPA. The error only occurs with AdHoc; development builds work fine. Questions: Is there anything else I should check regarding AdHoc provisioning for NetworkExtension DNS Proxy? Are there any known issues with AdHoc and NetworkExtension on recent iOS versions? Is there a way to get more detailed diagnostics from the system about why the permission is denied? Could this be a bug in iOS, or am I missing a subtle configuration step? Any help or suggestions would be greatly appreciated. Thank you!
Replies
1
Boosts
0
Views
136
Activity
Jun ’25
Debug Tunnel.appEx on mac Designed for iPad
Does someone know how to debug ios app on mac designed for iPad? i can debug on real iOS device but cant attach Tunnel to work for mac.
Replies
3
Boosts
0
Views
173
Activity
Apr ’25
Content Filters on devices without family controls authorisation.
I’m working on an iOS parental-control app that needs to block specific network traffic (e.g. certain domains or URLs). We’ve already obtained the Family Controls entitlement (since our app is explicitly a parental-control solution), but we do not use MDM to supervise devices. In testing, our NEFilterDataProvider extension only activates when the device is enrolled under a managed Family Controls profile. I am aware that we can use a PacketTunnel to achieve this but i was wondering if there is any simpler solution to this? Thanks for you time!
Replies
4
Boosts
0
Views
221
Activity
Jun ’25
Ping without CFSockets
All of our uses of CFSockets have started causing crashes in iOS 16. They seem to be deprecated so we are trying to transition over to using the Network framework and NWConnection to try to fix the crashes. One of our uses of them is to ping a device on the local network to make sure it is there and online and provide a heartbeat status in logs as well as put the application into a disabled state if it is not available as it is critical to the functionality of the app. I know it is discouraged to disable any functionality based on the reachability of a resource but this is in an enterprise environment where the reachability of this device is mission critical. I've seen other people ask about the ability to ping with the Network framework and the answers I've found have said that this is not possible and pointed people to the SimplePing sample code but it turns out our existing ping code is already using this technique and it is crashing just like our other CFSocket usages, inside CFSocketInvalidate with the error BUG IN CLIENT OF LIBPLATFORM: Trying to recursively lock an os_unfair_lock. Is there any updated way to perform a ping without using the CFSocket APIs that now seem to be broken/unsupported on iOS 16?
Replies
7
Boosts
0
Views
2.1k
Activity
Mar ’26
Enhancing NEFilterPacketProvider with Process-Level Filtering
Our application currently uses NEFilterPacketProvider to filter network traffic based on Layer 4 rules (5-tuple: source IP, destination IP, source port, destination port, and protocol) on a packet-by-packet basis. We now want to extend this filtering to also consider the associated process—for example, allowing traffic from a specific source IP to a destination IP and port only if it's associated with a specific local process. That is, we’d like to make filtering decisions not just based on the 5-tuple, but also on the identity of the process either sending or receiving the traffic. We’ve looked into NEFilterSocketProvider, which does expose Layer 7 information such as process identifiers. However, it doesn’t seem to be tightly synchronized with the packet flow handled by NEFilterPacketProvider. As a result, there’s a risk that we might only get process information after the TCP handshake is complete, or before the socket is fully bound—at which point some of the 5-tuple fields (such as the local port) may still be unavailable. What we need is a way to correlate the 5-tuple with the relevant process name (either sender or receiver) at the time the first packet—e.g., a SYN packet—is about to be sent or received. Is there a recommended way to achieve this kind of early, process-aware filtering using NetworkExtension APIs?
Replies
1
Boosts
0
Views
106
Activity
Jun ’25
wifi connect fail
Dear Apple: In our app, we will call the - (void) applyConfiguration:(NEHotspotConfiguration *) configuration completionHandler:(void (^)(NSError * error)) completionHandler; interface of NEHotspotConfigurationManager on Apple devices. However, we are encountering a problem where the connection to the 2.4G hotspot fails, and the error is nil when it fails. We checked the Wi-Fi air interface and found that the Apple phone does not send a probe request before connecting to the hotspot. However, we are unclear why the Apple device does not send the probe request frame. Could you please help us understand when the probe request frame is not sent during the hotspot connection and how to trigger it to send the probe request frame every time? Thank you.
Replies
4
Boosts
0
Views
149
Activity
May ’25
Multicast Entitlement Inquiry
Hi, I had a few questions regarding the multicast networking entitlement. What are the criteria for approval? Do ad-hoc multicast protocols fall under the approval criteria? How long do approvals for multicasting generally take?
Replies
2
Boosts
0
Views
132
Activity
Jun ’25
IOS app on MacOS 15 local network access
Our app is developed for iOS, but some users also run it on macOS (as an iOS app via Apple Silicon). The app requires local network permission, which works perfectly on iOS. Previously, the connection also worked fine on macOS, but since the recent macOS update, the app can no longer connect to our device. Additionally, our app on macOS doesn't prompt for local network permission at all, whereas it does on iOS. Is this a known issue with iOS apps running on macOS? Has anyone else experienced this problem, or is there a workaround? Any help would be appreciated!
Replies
9
Boosts
0
Views
951
Activity
Oct ’25
Split tunnel w/o changing route table
I've built a VPN app that is based on wireguard on macOS (I have both AppStore ver. and Developer ID ver). I want to achieve split tunneling function without changing the system route table. Currently, I'm making changes in PacketTunnelProvider: NEPacketTunnelProvider. It has included/excluded routes that function as a split tunnel, just that all changes are immediately reflected on the route table: if I run netstat -rn in terminal, I would see all rules/CIDRs I added, displayed all at once. Since I have a CIDR list of ~800 entries, I'd like to avoid changing the route table directly. I've asked ChatGPT, Claude, DeepSeek, .etc. An idea was to implement an 'interceptor' to intercept all packets in packetFlow(_:readPacketsWithCompletionHandler:), extract the destination IP from each packet, check if it matches your CIDR list, and either reinject it back to the system interface (for local routing) or process it through your tunnel. Well, LLMs could have hallucinations and I've pretty new to macOS programming. I'm asking to make sure I'm on the right track, not going delusional with those LLMs :) So the question is, does the above method sounds feasible? If not, is it possible to achieve split tunneling without changing the route table?
Replies
4
Boosts
0
Views
136
Activity
Jun ’25
URLSessionWebSocketTask reports closeCode as invalid when state is completed
I am using a URLSessionWebSocketTask. When trying to receive messages while the app is backgrounded, the receive() method fails with an NSError where the domain is NSPOSIXErrorDomain and the code is ECONNABORTED. That behavior is good. However, when this happens, the URLSessionWebSocketTask reports a closeCode of invalid, which is supposed to denote that the connection is still open. However, the connection state property is reporting completed. I feel that the closeCode property should be reporting something like abnormalClosure in this case. Either way, this seems like a bug or the documentation is incorrect.
Replies
2
Boosts
3
Views
220
Activity
Apr ’25
iPhone App 进行网络请求的时候出错,报错信息为:未能完成该操作。设备上无剩余空间
1、已经检查过手机的存储空间,还有一百多G的空间。app端进行网络接口情况的时候报错了,报错信息如下: Error : Error Domain=NSPOSIXErrorDomain Code=28 "No space left on device" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <7DB1CBFD-B9BE-422D-9C9A-78D8FC04DC1B>.<76>, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=28, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <7DB1CBFD-B9BE-422D-9C9A-78D8FC04DC1B>.<76>" ), _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: pdp_ip0[lte], ipv4, ipv6, dns, expensive, estimated upload: 65536Bps, uses cell} 2、手机型号是iPhone 15 Plus,iOS 17.6.1
Replies
2
Boosts
0
Views
419
Activity
Jun ’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
Real-time audio application on locked device
I would like to inquire about the feasibility of developing an iOS application with the following requirements: The app must support real-time audio communication based on UDP. It needs to maintain a TCP signaling connection, even when the device is locked. The app will run only on selected devices within a controlled (closed) environment, such as company-managed iPads or iPhones. Could you please clarify the following: Is it technically possible to maintain an active TCP connection when the device is locked? What are the current iOS restrictions or limitations for background execution, particularly related to networking and audio? Are there any recommended APIs or frameworks (such as VoIP, PushKit, or Background Modes) suitable for this type of application?
Replies
1
Boosts
0
Views
196
Activity
Jun ’25
Can NWBrowser be used in an AppIntent extension? NoAuth
I have an iOS app that connects to a server running on macOS by leveraging NWListener & NWBrowser. It also makes use of the peerToPeer functionality / AWDL offered via the Network framework. This works great in the iOS app. Now I would like to add support for Shortcuts / App Intents in general. The NWConnection on its own is also working great in the App Intent, but only if I provide the host/port manually, which means I can't use the peer to peer functionality. If I try to run my NWBrowser in the AppIntent it immediately changes its state to failed with a NoAuth (-65555) error: nw_browser_cancel [B1517] The browser has already been cancelled, ignoring nw_browser_cancel(). nw_browser_fail_on_dns_error_locked [B1518] DNSServiceBrowse failed: NoAuth(-65555) NWClientManager: Browser failed: -65555: NoAuth I haven't found documentation/information on whether NWBrowser should work in an AppIntent extension or not.
Replies
1
Boosts
0
Views
129
Activity
Apr ’25
Entitlement Request Support
We require the following Network Extension entitlements without the -systemextension suffix: packet-tunnel-provider app-proxy-provider Our application uses the legacy NetworkExtension framework, not the newer System Extensions. Although our provisioning profile has been approved by Apple, the entitlements are still being suffixed automatically with -systemextension. Since our code is built on the legacy NetworkExtension framework, this causes VPN functionality to break. Target platforms: macOS 14 & 15 (distributed outside the Mac App Store via a .pkg installer). Is there a way to use the original (non-systemextension) entitlements in this setup?
Replies
3
Boosts
0
Views
278
Activity
Jun ’25
Content Filter: sourceAppAuditToken empty only for Firefox
Starting on macOS Sequoia, flows originated in Firefox have an empty sourceAppAuditToken. Other apps contain a valid token. Background: in order to fetch process info for a certain NEFilterFlow, my content filter extension uses sourceAppAuditToken, audit_token_to_pid() and proc_* (as recommended in #126820). When that fails, we use SecCodeCopyGuestWithAttributes, recommended in some other thread as a better alternative. Both approaches break when the sourceAppAuditToken is empty since they need the pid. Debugging: My logging shows audit token is empty for Firefox Typical logs from com.apple.networkextension also indicate it fails to fetch the same info I'm looking for: com.apple.networkextension debug 11:22:07.024588-0300 Fetching appInfo from cache for pid: 948 uuid: 5C40B765-C6C9-3641-A822-2BC44D264361 bundle id: (null) com.apple.networkextension debug 11:22:07.024657-0300 Calling delegate lookup handler with pid: 948, uuid: 5C40B765-C6C9-3641-A822-2BC44D264361, bundleID: (null) com.apple.networkextension debug 11:22:07.025856-0300 Could not look up appInfo for pid: 948 bundle id: (null) uuid: 5C40B765-C6C9-3641-A822-2BC44D264361 com.apple.networkextension error 11:22:07.025897-0300 Could not find app info, return the original flow without filling in app info Handling new flow: identifier = D89B5B5D-793C-4940-D992-4E90F2AD1900 procPID = 953 eprocPID = 948 direction = outbound inBytes = 0 outBytes = 0 signature = {length = 32, bytes = 0x4afeafde b484aa0c c5cb8698 0567343d ... 7cdee33e 135666dd } socketID = 19adf2904e92d9 localEndpoint = 0.0.0.0:0 remoteEndpoint = 17.33.202.170:443 protocol = 6 family = 2 type = 1 procUUID = 0C68E603-967E-3643-B225-378BD2A655F7 eprocUUID = 5C40B765-C6C9-3641-A822-2BC44D264361 Perhaps there's a bug when generating the audit token or could it be something with the Firefox signature? I double-checked Firefox and it seems fine: $ codesign --verify --verbose /Applications/Firefox.app /Applications/Firefox.app: valid on disk /Applications/Firefox.app: satisfies its Designated Requirement Not sure if relevant, but codesign with -dv showed different flags in CodeDirectory when compared to chrome: codesign -dv /Applications/Firefox.app ... CodeDirectory v=20500 size=863 flags=0x10000(runtime) hashes=18+5 ... Versus chrome CodeDirectory v=20500 size=1821 flags=0x12a00(kill,restrict,library-validation,runtime) hashes=46+7 location=embedded
Replies
3
Boosts
0
Views
570
Activity
Aug ’25
Can't find server for API Endpoint that works.
Hi, I am making a AI-Powered app that makes api requests to the openai API. However, for security, I set up a vercel backend that handles the API calls securely, while my frontend makes a call to my vercel-hosted https endpoint. Interestingly, whenever I try to make that call on my device, an iPhone, I get this error: Task <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10> finished with error [-1003] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={_kCFStreamErrorCodeKey=-72000, NSUnderlyingError=0x1435783f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorDomainKey=10, _kCFStreamErrorCodeKey=-72000, _NSURLErrorNWResolutionReportKey=Resolved 0 endpoints in 3ms using unknown from query, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: pdp_ip0[lte], ipv4, ipv6, dns, expensive, uses cell}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <91AE4DE0-2845-4348-89B4-D3DD1CF51B65>.<10>" ), NSLocalizedDescription=A server with the specified hostname could not be found., NSErrorFailingURLStringKey=https://[my endpoint], NSErrorFailingURLKey=https://[my endpoint], _kCFStreamErrorDomainKey=10} I'm completely stuck because when I directly make https requests to other api's like openai's endpoint, without the proxy, it finds the server completely fine. Running my endpoint on terminal with curl also works as intended, as I see api key usages. But for some reason, on my project, it does not work. I've looked through almost every single post I could find online, but a lot all of the solutions are outdated and unhelpful. I'm willing to schedule a call, meeting, whatever to resolve this issue and get help more in depth as well.
Replies
1
Boosts
0
Views
163
Activity
Jun ’25
iOS not launching my app network extension, it seemingly isn't crashing it either
My personal project is a bit further along however after not being able to get this to work in my app I fell back to a much simpler/proven implementation out there. There is this project on GitHub with a guide that implements a barebones app extension with packet tunneling. I figure this can give us common ground. After changing the bundle and group identifiers to all end with -Caleb and or match up I tried running the app. The app extension does not work whatsoever and seemingly for reasons that are similar to my personal project. If I pull up the console and filter for the subsystem (com.github.kean.vpn-client-caleb.vpn-tunnel) I see the following. First you see installd installing it 0x16ba5f000 -[MIUninstaller _uninstallBundleWithIdentity:linkedToChildren:waitForDeletion:uninstallReason:temporaryReference:deleteDataContainers:wasLastReference:error:]: Destroying container com.github.kean.vpn-client-caleb.vpn-tunnel with persona 54D15361-A614-4E0D-931A-0953CDB50CE8 at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65 And then installd says this (No idea what it means) 0x16b9d3000 -[MIInstallationJournalEntry _refreshUUIDForContainer:withError:]: Data container for com.github.kean.vpn-client-caleb.vpn-tunnel is now at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65 Concerningly runningboardd seems to immediately try and stop it? Executing termination request for: <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> [app<com.github.kean.vpn-client-caleb(54D15361-A614-4E0D-931A-0953CDB50CE8)>:1054] Terminating with context: <RBSTerminateContext| explanation:installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y reportType:None maxTerminationResistance:Absolute attrs:[ <RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> allow:(null)> ]> Then runningboardd leaves a cryptic message Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd>:244] with description <RBSAssertionDescriptor| "installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y" ID:33-244-5222 target:system attributes:[ <RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {( "com.github.kean.vpn-client-caleb", "com.github.kean.vpn-client-caleb.vpn-tunnel" )}>> allow:(null)> ]> And that seems to be all I have to go off of.... If I widen my search a bit I can see backboardd saying things like Connection removed: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{ HighFrequency = 1; bundleID = "com.github.kean.vpn-client-caleb"; pid = 1054; } state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE Or Removing client connection <BKHIDClientConnection: 0xbf9828cd0; IOHIDEventSystemConnectionRef: 0xbf96d9600; vpid: 1054(vAF7); taskPort: 0x5D777; bundleID: com.github.kean.vpn-client-caleb> for client: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{ HighFrequency = 1; bundleID = "com.github.kean.vpn-client-caleb"; pid = 1054; } state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE source:HID There's really nothing in the sysdiagnose either. No crash no nothing. I am stumped. Any idea what might be going wrong for me here? Has something about the way app extensions or sandbox rules work changed in later OSes?
Replies
1
Boosts
0
Views
225
Activity
Apr ’25
Optimization Suggestion: Update Queue Prioritization by Payload Size.
Dear Developers, I would like to suggest an optimization for the logic governing the download and installation queue for app updates. Currently, when multiple applications are awaiting updates, the prioritization does not appear to consider the update payload size. My proposal is to implement a logic that prioritizes the download and installation of updates with a smaller delta size (fewer MB) before those with a larger delta. Practical Example: A 1MB update would be processed before a 500MB update, even if their arrival order in the queue was inverted. Potential Benefits: Perceived Speed Optimization (UX): Users would gain access to functional applications more quickly, especially in scenarios with multiple pending updates. Network Efficiency: In limited or intermittent bandwidth scenarios, completing smaller downloads first can reduce the chance of download failures and optimize network resource utilization. Device Resource Management: Frees up temporary storage and processing resources more rapidly for smaller updates. I believe this optimization would bring significant gains in terms of User Experience (UX) and the operational efficiency of the platform. Thank you for your attention and consideration. Sincerely,
Replies
1
Boosts
0
Views
166
Activity
Jun ’25
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.3k
Activity
1w