Hi there,
We are facing some issues regarding TLS connectivity:
Starting with iOS 26, the operating system refuses to open TLS sockets to local devices with self-signed certificates over Wi-Fi. In this situation, connection is no longer possible, even if the device is detected on the network with Bonjour.
We have not found a workaround for this problem.
We've tryied those solutions without success:
Added the 'NSAppTransportSecurity' key to the info.plist file, testing all its items, such as "NSAllowsLocalNetworking", "NSExceptionDomains", etc.
Various code changes to use properties such as "sec_protocol_options_set_local_identity" and "sec_protocol_options_set_tls_server_name" to no avail.
Brutally import the certificate files into the project and load them via, for example, "Bundle.main.url(forResource: "nice_INTERFACE_server_cert", withExtension: "crt")", using methods such as sec_trust_copy_ref and SecCertificateCopyData.
Download the .pem or .crt files to the iPhone, install them (now visible under "VPN & Device Management"), and then flag them as trusted by going to "Settings -> General -> Info -> Trust". certificates"
The most critical part seems to be the line
sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { $2(true) }, queue)
whose purpose is to bypass certificate checks and validate all of them (as apps already do). However, on iOS26, if I set a breakpoint on leg$2(true),` it never gets there, while on iOS 18, it does.
I'll leave as example the part of the code that was tested the most below. Currently, on iOS26, the handler systematically falls back to .cancelled:
func startConnection(host: String, port: UInt16) {
self.queue = DispatchQueue(label: "socketQueue")
let tlsOptions = NWProtocolTLS.Options()
sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { $2(true) }, queue)
let parameters = NWParameters(tls: tlsOptions)
self.nwConnection = NWConnection(host: .init(host), port: .init(rawValue: port)!, using: parameters)
self.nwConnection.stateUpdateHandler = { [weak self] state in
switch state {
case .setup:
break
case .waiting(let error):
self?.connectionDidFail(error: error)
case .preparing:
break
case .ready:
self?.didConnectSubject.onNext(Void())
case .failed(let error):
self?.connectionDidFail(error: error)
case .cancelled:
self?.didDisconnectSubject.onNext(nil)
@unknown default:
break
}
}
self.setupReceive()
self.nwConnection.start(queue: queue)
}
These are the prints made during the procedure. The ones with the dot are from the app, while the ones without are warnings/info from Xcode:
🔵 INFO WifiNetworkManager.connect():52 - Try to connect onto the interface access point with ssid NiceProView4A9151_AP
🔵 INFO WifiNetworkManager.connect():68 - Connected to NiceProView4A9151_AP
tcp_output [C13:2] flags=[R.] seq=215593821, ack=430284980, win=4096 state=CLOSED rcv_nxt=430284980, snd_una=215593821
nw_endpoint_flow_failed_with_error [C13 192.168.0.1:443 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], dns, uses wifi, LQM: unknown)] already failing, returning
nw_connection_copy_protocol_metadata_internal_block_invoke [C13] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C13] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_connected_local_endpoint_block_invoke [C13] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection
nw_connection_copy_connected_remote_endpoint_block_invoke [C13] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C14] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C14] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_connected_local_endpoint_block_invoke [C14] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection
nw_connection_copy_connected_remote_endpoint_block_invoke [C14] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection
[C14 192.168.0.1:443 tcp, tls, attribution: developer] is already cancelled, ignoring cancel
[C14 192.168.0.1:443 tcp, tls, attribution: developer] is already cancelled, ignoring cancel
nw_connection_copy_protocol_metadata_internal_block_invoke [C15] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C15] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_connected_local_endpoint_block_invoke [C15] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection
nw_connection_copy_connected_remote_endpoint_block_invoke [C15] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C16] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C16] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
nw_connection_copy_connected_local_endpoint_block_invoke [C16] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection
nw_connection_copy_connected_remote_endpoint_block_invoke [C16] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection
[C16 192.168.0.1:443 tcp, tls, attribution: developer] is already cancelled, ignoring cancel
[C16 192.168.0.1:443 tcp, tls, attribution: developer] is already cancelled, ignoring cancel
🔴 ERROR InterfaceDisconnectedViewModel.connect():51 - Sequence timeout.
Topic:
App & System Services
SubTopic:
Networking
Tags:
Foundation
Developer Tools
Nearby Interaction
iOS