I'm struggling to understand why the async-await version of URLSession download task APIs do not call the delegate functions, whereas the old non-async version that returns a reference to the download task works just fine.
Here is my sample code:
class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
// This only prints the percentage of the download progress.
let calculatedProgress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
let formatter = NumberFormatter()
formatter.numberStyle = .percent
print(formatter.string(from: NSNumber(value: calculatedProgress))!)
}
}
// Here's the VC.
final class DownloadsViewController: UIViewController {
private let url = URL(string: "https://pixabay.com/get/g0b9fa2936ff6a5078ea607398665e8151fc0c10df7db5c093e543314b883755ecd43eda2b7b5178a7e613a35541be6486885fb4a55d0777ba949aedccc807d8c_1280.jpg")!
private let delegate = DownloadDelegate()
private lazy var session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
// for the async-await version
private var task: Task<Void, Never>?
// for the old version
private var downloadTask: URLSessionDownloadTask?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
task?.cancel()
task = nil
task = Task {
let (_, _) = try! await session.download(for: URLRequest(url: url))
self.task = nil
}
// If I uncomment this, the progress listener delegate function above is called.
// downloadTask?.cancel()
// downloadTask = nil
// downloadTask = session.downloadTask(with: URLRequest(url: url))
// downloadTask?.resume()
}
}
What am I missing here?
Networking
RSS for tagExplore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
We have an application which is written in Swift, which activates two network extensions (Content Filter, Transparent Proxy). We want to use MDM deployment for these network system extensions.
For Content Filter, we already have Jamf Profile which has Web Content Filter payload and it works fine.
Our Transparent Proxy module is a system extension, which is exposing an app proxy provider interface (We are using NETransparentProxyProvider class and in extension’s Info.plist we use com.apple.networkextension.app-proxy key.) We don’t have any remote server setup to forward the traffic, instead we open a connection with a certain localhost:port to redirect the traffic which is received in our transparent proxy. We have another module that listens to the particular localhost:port to process the traffic further.
We are unable to find the appropriate payload in any of the Profile Editor applications like Apple Configurator, iMazing Profile Editor and Jamf Pro that correctly describes our setup.
As per https://developer.apple.com/documentation/devicemanagement/vpn/transparentproxy documentation, we noticed that we can use the VPN payload with app-proxy as Provider Type for Transparent Proxy.
Here are the list of issues encountered with different MDM solutions.
**AppleConfigurator: **
We were able to install the profile created via Apple Configurator. However when we install our product (which has the above mentioned system extensions), the Transparent Proxy added by our product fails to map with the installed profile. User has to provide the credentials and follow the steps while installing the extension via the product.
Attached the screenshot of "Network->Filters" screen and the profile for reference.
Profile Created using Apple Configurator
iMazing Profile Editor:
Unable to install the profile created using iMazing Profile Editor.
Attached the screenshot of error and the profile for reference:
Profile Created Using iMazing Profile Editor
Jamf Pro:
We were able to install the profile created via Jamf Pro and also while in stalling our product the Transparent Proxy gets mapped with the one which is installed via profile. However after that the network is broken and hence unable to browse anything.
Attached the profile for reference.
Profile Created using Jamf Pro
What should be the correct profile payload to use for our Transparent Proxy?
Topic:
App & System Services
SubTopic:
Networking
Tags:
Network Extension
System Extensions
Device Management
Summary
NetworkConnection<WebSocket> in iOS 26 Network framework throws POSIXErrorCode(rawValue: 22): Invalid argument when receiving WebSocket ping (opcode 9) or pong (opcode 10) control frames. This prevents proper WebSocket keep-alive functionality.
Environment
iOS 26.0 (Simulator)
macOS 26.1
Xcode 26.0
Note: This issue was initially discovered on iOS 26 Simulator. The same behavior was confirmed on macOS 26, suggesting a shared bug in the Network framework. The attached sample code is for macOS for easier reproduction.
Description
When using the new NetworkConnection<WebSocket> API introduced in iOS 26 or macOS 26, the receive() method throws EINVAL error whenever a ping or pong control frame is received from the server.
This is a critical issue because:
WebSocket servers commonly send ping frames to keep connections alive
Clients send ping frames to verify connection health
The receive callback never receives the ping/pong frame - the error occurs before the frame reaches user code
Steps to Reproduce
Create a WebSocket connection to any server that supports ping/pong (e.g., wss://echo.websocket.org):
import Foundation
import Network
// MARK: - WebSocket Ping/Pong EINVAL Bug Reproduction
// This sample demonstrates that NetworkConnection<WebSocket> throws EINVAL
// when receiving ping or pong control frames.
@main
struct WebSocketPingPongBug {
static func main() async {
print("=== WebSocket Ping/Pong EINVAL Bug Reproduction ===\n")
do {
try await testPingPong()
} catch {
print("Test failed with error: \(error)")
}
}
static func testPingPong() async throws {
let host = "echo.websocket.org"
let port: UInt16 = 443
print("Connecting to wss://\(host)...")
let endpoint = NWEndpoint.hostPort(
host: NWEndpoint.Host(host),
port: NWEndpoint.Port(rawValue: port)!
)
try await withNetworkConnection(to: endpoint, using: {
WebSocket {
TLS {
TCP()
}
}
}) { connection in
print("Connected!\n")
// Start receive loop in background
let receiveTask = Task {
var messageCount = 0
while !Task.isCancelled {
do {
let (data, metadata) = try await connection.receive()
messageCount += 1
print("[\(messageCount)] Received frame - opcode: \(metadata.opcode)")
if let text = String(data: data, encoding: .utf8) {
print("[\(messageCount)] Content: \(text)")
} else {
print("[\(messageCount)] Binary data: \(data.count) bytes")
}
} catch let error as NWError {
if case .posix(let code) = error, code == .EINVAL {
print("❌ EINVAL error occurred! (POSIXErrorCode 22: Invalid argument)")
print(" This is the bug - ping/pong frame caused EINVAL")
// Continue to demonstrate workaround
continue
}
print("Receive error: \(error)")
break
} catch {
print("Receive error: \(error)")
break
}
}
}
// Wait for initial message from server
try await Task.sleep(for: .seconds(2))
// Test 1: Send text message (should work)
print("\n--- Test 1: Sending text message ---")
try await connection.send("Hello, WebSocket!")
print("✅ Text message sent")
try await Task.sleep(for: .seconds(1))
// Test 2: Send ping (pong response will cause EINVAL)
print("\n--- Test 2: Sending ping frame ---")
print("Expecting EINVAL when pong is received...")
let pingMetadata = NWProtocolWebSocket.Metadata(opcode: .ping)
try await connection.ping(Data()) {
pingMetadata
}
print("✅ Ping sent, waiting for pong...")
// Wait for pong response
try await Task.sleep(for: .seconds(2))
// Cleanup
receiveTask.cancel()
print("\n=== Test Complete ===")
print("If you saw 'EINVAL error occurred!' above, the bug is reproduced.")
}
}
}
The receive() call fails with error when pong arrives:
❌ EINVAL error occurred! (POSIXErrorCode 22: Invalid argument)
Test Results
Scenario
Result
Send/receive text (opcode 1)
✅ OK
Client sends ping, receives pong
❌ EINVAL on pong receive
Expected Behavior
The receive() method should successfully return ping and pong frames, or at minimum, handle them internally without throwing an error. The autoReplyPing option should allow automatic pong responses without disrupting the receive loop.
Actual Behavior
When a ping or pong control frame is received:
The receive() method throws NWError.posix(.EINVAL)
The frame never reaches user code (no opcode check is possible)
The connection remains valid, but the receive loop is interrupted
Workaround
Catch the EINVAL error and restart the receive loop:
while !Task.isCancelled {
do {
let received = try await connection.receive()
// Process message
} catch let error as NWError {
if case .posix(let code) = error, code == .EINVAL {
// Control frame caused EINVAL, continue receiving
continue
}
throw error
}
}
This workaround allows continued operation but:
Cannot distinguish between ping-related EINVAL and other EINVAL errors
Cannot access the ping/pong frame content
Cannot implement custom ping/pong handling
Impact
WebSocket connections to servers that send periodic pings will experience repeated EINVAL errors
Applications must implement workarounds that may mask other legitimate errors
Additional Information
Packet capture confirms ping/pong frames are correctly transmitted at the network level
The error occurs in the Network framework's internal processing, before reaching user code
We are developing an enterprise app that connects to a local server.
It uses simple URLSessions. There is a view in the app where you enter the server url (IP address) and a connection check is made.
iOS asks for permission to access the local network.
Everything works. If the server is reachable, the connection info is saved.
Recently we encountered a very strange issue:
We also have a beta version of this app.
If we first install the normal version on a device, enter the server IP, save, and then install the beta version and do the same there: It does not get a connection (it waits for the timeout).
The strange part is: If I try to configure the connection in the normal version again, it also does not work, it just waits for the timeout.
The really strange part: When I delete the beta version, while the normal version is waiting for its connection, the connection succeeds immediately.
Both versions have a different display name, bundle id.
I also tried using a device that is not in our MDM: same problem.
Even the iOS version seems to have no impact: I tried on iOS 15, 18 and 26.
Is there an explanation and hopefully also a solution to this problem?
Topic:
App & System Services
SubTopic:
Networking
In my application, there is a Network Extension with the bundle ID com.xxx.agent.yyy.zzz.ne. There is a user upgraded their system to macOS Sequoia 15.3, they faced an issue where enabling this Network Extension failed. Even after uninstalling the application and the Network Extension, restarting the system, and reinstalling multiple times, the enabling process still failed.
it alert: Failed to enable the Network Extension.
When checking the status via "systemextension list", it always shows "activated waiting for user".
This shows the normal enabling process log:
This shows the log when the enabling fails upon clicking. Strangely enough, there is no activation operation log when it fails. What could be the problem?
we use the api as
NEHotspotConfigurationManager.shared.apply(hotspotConfig)
to join a wifi, but we find that in in iphone 17+, some user report the time to join wifi is very slow
the full code as
let hotspotConfig = NEHotspotConfiguration(ssid: sSSID, passphrase: sPassword, isWEP: false)
hotspotConfig.joinOnce = bJoinOnce
if #available(iOS 13.0, *) {
hotspotConfig.hidden = true
}
NEHotspotConfigurationManager.shared.apply(hotspotConfig) { [weak self] (error) in
guard let self else {
return
}
if let error = error {
log.i("connectSSID Error while configuring WiFi: \(error.localizedDescription)")
if error.localizedDescription.contains("already associated") {
log.i("connectSSID Already connected to this WiFi.")
result(["status": 0])
} else {
result(["status": 0])
}
} else {
log.i("connectSSID Successfully connected to WiFi network \(sSSID)")
result(["status": 1])
}
}
Normally it might only take 5-10 seconds, but on the iPhone 17+ it might take 20-30 seconds.
I’m using an NETransparentProxyProvider where I add UDP-53 rules to intercept DNS queries from a private application. These queries are resolved locally on the endpoint by returning a custom DNS response.
Example Rules look like this:
NENetworkRule(destinationHost: NWHostEndpoint(hostname: "mypaapp.com", port: 53),protocol:.UDP)
This works as expected through browser and ping.
handleNewUDPFlow/handleNewFlow with NEAppProxyUDPFlow gets called where custom dns response get written.
Using nslookup mypaapp.com doesn't works.
Why does this behaves differently for nslookup?
Hello,
We are facing an issue with performing a DTLS handshake when our iOS application is in the background. Our app (Vocera Collaboration Suite – VCS) uses secure DTLS-encrypted communication for incoming VoIP calls.
Problem Summary:
When the app is in the background and a VoIP PushKit notification arrives, we attempt to establish a DTLS handshake over our existing socket. However, the handshake consistently fails unless the app is already in the foreground. Once the app is foregrounded, the same DTLS handshake logic succeeds immediately.
Key Questions:
Is performing a DTLS handshake while the app is in the background technically supported by iOS?
Or is this an OS-level limitation by design?
If not supported, what is the Apple-recommended alternative to establish secure DTLS communication for VoIP flows without bringing the app to the foreground?
Any guidance or clarification from Apple engineers or anyone who has solved a similar problem would be greatly appreciated.
Thank you.
For a long time our app had this creation of a URLRequest:
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeout)
But since iOS 26 was released we started to get crashes in this call. It is created on a background thread.
Thread 10 Crashed:
0 libsystem_malloc.dylib 0x00000001920e309c _xzm_xzone_malloc_freelist_outlined + 864 (xzone_malloc.c:1869)
1 libswiftCore.dylib 0x0000000184030360 swift::swift_slowAllocTyped(unsigned long, unsigned long, unsigned long long) + 56 (Heap.cpp:110)
2 libswiftCore.dylib 0x0000000184030754 swift_allocObject + 136 (HeapObject.cpp:245)
3 Foundation 0x00000001845dab9c specialized _ArrayBuffer._consumeAndCreateNew(bufferIsUnique:minimumCapacity:growForAppend:) + 120
4 Foundation 0x00000001845daa58 specialized static _SwiftURL._makeCFURL(from:baseURL:) + 2288 (URL_Swift.swift:1192)
5 Foundation 0x00000001845da118 closure #1 in _SwiftURL._nsurl.getter + 112 (URL_Swift.swift:64)
6 Foundation 0x00000001845da160 partial apply for closure #1 in _SwiftURL._nsurl.getter + 20 (<compiler-generated>:0)
7 Foundation 0x00000001845da0a0 closure #1 in _SwiftURL._nsurl.getterpartial apply + 16
8 Foundation 0x00000001845d9a6c protocol witness for _URLProtocol.bridgeToNSURL() in conformance _SwiftURL + 196 (<compiler-generated>:974)
9 Foundation 0x000000018470f31c URLRequest.init(url:cachePolicy:timeoutInterval:) + 92 (URLRequest.swift:44)# Live For Studio
Any idea if this crash is caused by our code or if it is a known problem in iOS 26?
I have attached one of the crash reports from Xcode:
2025-10-08_10-13-45.1128_+0200-8acf1536892bf0576f963e1534419cd29e6e10b8.crash
I'm currently testing URLFilter for use in a macOS product. After calling loadFromPreferences, I set the following configuration parameters:
pirServerURL = URL(string: "http://localhost:8080")!
pirAuthenticationToken = "AAAA"
controlProviderBundleIdentifier = "{extension app bundle identifier}"
However, when I call saveToPreferences, I get an Invalid Configuration error.
Is there a way to determine which parameter is invalid or incorrectly set?
Also, I would appreciate any macOS-specific examples of using NEURLFilterManager, as most of the documentation I’ve found seems to focus on iOS.
Thank you.
We have a content filter system extension as part of our macOS app. The filter normally works correctly, activation and deactivation works as expected but occasionally we see an issue when the content filter is activated.
When this issues occurs, the filter activation appears to behave correctly, no errors are reported. Using "systemextensionsctl list" we see the filter is labelled as "[activated enabled]". However, the installed content filter executable does not run.
We have seen this issue on macOS 15.3 and later and on the beta macOS 26.1 RC.
It happens only occasionally but when it does there is no indication as to why the executable is not running. There are no crash logs or errors in launchd logs.
Both rebooting and deactivating/activating the filter do not resolve the issue. The only fix appears to be completely uninstalling the app (including content filter) and reinstalling.
I have raised a FB ticket, FB20866080.
Does anyone have any idea what could cause this?
In our system, when a user enables a mobile hotspot and the system connects to it, the system attempts to verify WIFI availability by sending an HTTP GET request to http://captive.apple.com.
Normally, the server returns:
HTTP Status: 200 (OK)
Content-Type: text/html
This has always been used as a sign of normal connectivity.
Issue:
Since last Friday, the server sometimes responds with:
Content-Type: application/octet-stream
When this occurs, our system determines that the network is unavailable and displays a connection warning (a “!” icon).
Question:
Has Apple recently made any backend or CDN configuration changes to captive.apple.com that could affect the response type?
Any advice how can we solve this problem?
Thanks!
Topic:
App & System Services
SubTopic:
Networking
I'm trying to implement support for grpc http/2 streams using NSURLSession. Almost everything works fine, data streaming is flowing from the server and from the client and responses are coming through my NSURLSessionTaskDelegate. I'm getting the responses and streamed data through the appropriate handlers (didReceiveData, didReceiveResponse).
However, I cannot seem to find an API to access the trailers expected by grpc. Specifically, the expected trailer "grpc-status: 0" is in the response, but after the data. Is there no way to gain access to trailers in the NSURLSession Framework?
I'm working with Apple's SimpleURLFilter sample project and consistently encountering an error when trying to implement the URL filter.
Here are the details:
Setup:
Downloaded the official SimpleURLFilter sample project from Apple
Set the developer team for both targets (main app and extension)
Built and ran the PIR server on my laptop using Docker as per the sample instructions
Built the iOS project on my iPhone running iOS 26.0.1
Server is accessible at my Mac's IP address on port 8080
Configuration:
PIR Server URL: http://[my-mac-ip]:8080
Authentication Token: AAAA (as specified in service-config.json)
Privacy Pass Issuer URL: (left empty)
Fail Closed: enabled
Code Changes:
The only modifications I made were:
Updated bundle identifiers to include my team identifier
Updated PIR server's service-config.json to match: com.example.apple-samplecode.SimpleURLFilter[TEAM_ID].url.filtering
Modified URLFilterControlProvider.swift:
Added existingPrefilterTag: String? parameter to fetchPrefilter() method
Added tag: "bloom_filter" parameter to NEURLFilterPrefilter initializer
Issue:
After configuring the filter and entering my passcode in Settings, I consistently see:
Received filter status change: <FilterStatus: 'starting'>
Received filter status change: <FilterStatus: 'stopped' errorMessage: 'The operation couldn't be completed. (NetworkExtension.NEURLFilterManager.Error error 9.)'>
Questions:
What does NEURLFilterManager.Error error 9 specifically indicate?
Could the URLFilterControlProvider modifications be causing this issue?
Are there debugging steps to get more detailed error information?
Any guidance would be appreciated!
Hello,
We've been working on an app that uses the new NEUrlFilter API and we've got a question.
Currently, the system is designed with the assumption that a single app == usecase == single remote database.
But what if we would like to give the user the ability to use different blocklists?
For example, the user may want to:
Block scam domains
Block tracking domains
Block adult domains
Or any composition of these 3
What should we do to give the user this option?
It seems that we could differentiate different databases by using different PIR service hostnames, but that would also mean that we'll have to send several requests for the same usecase but with different PIR service hostnames (and they'll all share the same app bundle ID). Will these requests be accepted then?
If not, is there an alternative?
PS: By sending a request I mean submitting this form
My app has local network permission on macOS Sequoia and works in most cases. I've noticed that after unlocking my MacBook Pro, the very first request will regularly fail with a No Route to Host. A simple retry resolves the issue, but I would have expected the very first request to succeed.
Is this is a known issue on macOS Sequoia or by design? I'd prefer not to add a retry for this particular request as the app is a network utility.
Topic:
App & System Services
SubTopic:
Networking
The environment:
macOS 12.0 ~ 15.6
A NetworkExtension NEFilterDataProvider configured with filterSockets = YES, filterPackets = NO, and it doesn't actually block any network connection.
QQMusic (download: https://y.qq.com/n/ryqq/download_detail/mac?ADTAG=YQQ) is constantly playing.
Any of the following operations can reproduce the issue:
Kill the NetworkExtension process and then restarted by the system.
Disable the NEFilterDataProvider, and then enable it.
When this problem occurs, there are two different phenomena on the NetworkExtension process:
It is zombie, or is in high CPU state (100%).
When the NetworkExtension process is zombie, obviously, the new network connections will enter it, and they can't be disposed by the old zombie process, so the network is disconnected.
Spindump-qqmusic-ne-zombie
When the NetworkExtension process is in high CPU state, its thread DispatchQueue "NEFilterExtensionProviderContext queue" is blocked in the kernel when calling close.
Spindump-qqmusic-ne-cpuhigh
In most cases, the network will recover after stopping QQ Music, that is the suspended zombie NetworkExtension process will exist or the cpu of it return to normal.
To reproduce the issue in a simple environment, I have tried many ways to simulate the network behavior of QQMusic, but all failed.
It seems that this issue is caused by UDP traffic of QQMusic, because everything is ok after blocking the UDP connections of QQMusic (the music is still playing at this time) in the NEFilterDataProvider.
We have developed a DNS filter based on NEDNSProxyProvider. It works great for a minute, then it stops responding. Our logs indicate that during the outage our extension gets DNS requests and formulates DNS responses that it hands back to the OS, but from outside of our code it is as though our extension is not responding.
While updating our test devices to iOS 26, we noticed that the connection between devices are flaky. Often when connecting to a Peer from a device running iOS 26 we can observe the invite coming through and when accepting said invite, both ends going to .connecting state and a while later going back to .notConnected within the peer(_ peerID: MCPeerID, didChange state: MCSessionState) function. This happens regularly and retrying the invitation process several times usually resolves it. Do anyone have any information or guidance on how to resolve this issue?
I have a Vision Pro app, which I intend to use Apple-Hosted Background Assets for some of my videos after watching:
https://developer.apple.com/videos/play/wwdc2025/325
I added a Apple-Hosted, Managed extension.
New Target -> Background Download -> Apple-Hosted, Managed
After creating an Archive, I tried uploading it to TestFlight, it complains about a DTPlatformName error in my Info.plist. So I added the following :
<key>DTPlatformName</key>
<string>xros</string>
With which, I managed to upload the app with the extension to TestFlight. However, when I tried installing the app on TestFlight to Vision Pro, it gives me an error that says the app cannot be verified.
Any help or pointers is greatly appreciated.
Info.plist
Entitlements