We have a device which is an appliance and we are developing a control interface app for macOS and iOS/iPadOS.
How can we set up our iOS application to grab information from a local network device while it is in the background in order to show notifications?
Communication between the Apple device and our device is via local networking and the device is designed to be used on networks without internet connections. On networks with internet connections we could forward events from the device, via a server and APNS push notifications, but that isn't valid here.
Events occur on our device and are forwarded to clients, who are subscribed to Server-Sent Events. On macOS this works well and the application can receive updates and show Notification Center notifications fine.
On iOS we are using a BGAppRefreshTaskRequest with time interval set to 1 minute, but it appears that we get scheduled only every few hours. This isn't very useful as notifications just arrive in batches rather than in a timely manner. All normal networking is closed when the app goes into the background, so we cannot keep the SSE request open.
Another idea which we haven't tried yet: Creating a new endpoint on the device which keeps the connection open until a notification arrives, then using background URLSession to poll on that endpoint. Would that work? It seems like a mis-use of the API perhaps?
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.
Post
Replies
Boosts
Views
Activity
I'm working with an app that was developed with the CocoaAsyncSocket library, which uses <sys/socket.h> style socket programming. We have a customer where certain features are not working if iOS "adapts" to v3 IGMP but seems to work if v2 IGMP is used.
I can't say I understand all the low level aspects and am trying to inform myself. I've been searching online to find out about socket and IGMP but don't see anything coming up.
Is there a way to force iOS to use IGMP v2 for broadcast? Our company also makes a hardware communicator and their code forces v2 and we've been asked to see if we can do the same.
Thanks for any leads on where to go to inform myself. more or code snippets on how to do this.
In our setup, our Transparent Proxy (call it TP1) funnels traffic to a helper process running on the same machine (call it Helper), which then actually sends out the traffic to the wider Internet. Now say there's another Transparent Proxy, TP2, on the same machine.
Assuming TP1 gets hold of the traffic first, the sequence would look like so:
Safari --> TP1 --> Helper --> TP2
We want to make it appear to TP2 that the incoming traffic is from Safari, rather than from the Helper process.
We are aware of the Network framework's setMetadata API, but this does not look appropriate for us to use here. The Helper process is pre-existing Golang code, which at best can interface with "pure" (ie BSD) sockets-based C code. In order to use the setMetadata API, looks like we will need to rewrite the entire networking logic to use nw_connection_t (or similar) API, which is too much work, so is infeasible for us to use.
Is there a way to make the setMetadata API work at a socket level? e.g., associate the metadata with a socket so that whatever data is sent out on the socket by the Helper will seem to TP2 to be coming from the desired source process.
Assuming there isn't such a way, please consider this an Enhancement Request to make it so!
Also, this reveals another complication: If and when this Enhancement is implemented, our own TP1 (which interepted the traffic in the first place) would end up thinking that the traffic is from Safari, so ends up re-intercepting it, causing a loop.
Safari --> TP1 --> Helper (invokes setMetadata) --> TP1 --> Helper ...
Which leads to the next Enhancement Request: Please extend the API to allow setting of the "last-hop" source process in addition to the original source application. If the last-hop source process info is set, our TP1 can query this property, see that it's coming from our own Helper process, and skip interception.
In summary, here are the Enhancement Requests:
Allow setMetadata API to work at a socket level
Allow setting of "last-hop" source process in the metadata, in addition to the original source application
More succinctly, please allow setting of metadata to cater to cases where the actual egress happens via a (different) helper process that uses pure C sockets based API.
I have also filed this as a Feedback with Apple, at FB16048393.
Hi,
One of our customers is seeing a crash in our Content Filter in our network system extension. We're kind of at a loss for the cause of this as only one specific person is running into this and we're not at all in the stacktrace, out of the hundreds of others deployed with our extension.
It would be greatly appreciated if we could have any help in diagnosing this issue. Attached is the crash report, and below is the crashing stacktrace. If this crash log is not sufficient, I have many more from the customer that I can attatch here.
crash.txt
Thread 4 Crashed:: Dispatch queue: NEFilterExtensionProviderContext queue
0 libsystem_kernel.dylib 0x18cd4e600 __pthread_kill + 8
1 libsystem_pthread.dylib 0x18cd86f70 pthread_kill + 288
2 libsystem_c.dylib 0x18cc93908 abort + 128
3 libc++abi.dylib 0x18cd3d44c abort_message + 132
4 libc++abi.dylib 0x18cd2ba40 demangling_terminate_handler() + 348
5 libobjc.A.dylib 0x18c9d13e4 _objc_terminate() + 156
6 libc++abi.dylib 0x18cd3c710 std::__terminate(void (*)()) + 16
7 libc++abi.dylib 0x18cd3c6b4 std::terminate() + 108
8 libdispatch.dylib 0x18cbd466c _dispatch_client_callout + 40
9 libdispatch.dylib 0x18cbdbc60 _dispatch_lane_serial_drain + 744
10 libdispatch.dylib 0x18cbdc79c _dispatch_lane_invoke + 432
11 libdispatch.dylib 0x18cbe77e8 _dispatch_root_queue_drain_deferred_wlh + 288
12 libdispatch.dylib 0x18cbe7034 _dispatch_workloop_worker_thread + 540
13 libsystem_pthread.dylib 0x18cd833d8 _pthread_wqthread + 288
14 libsystem_pthread.dylib 0x18cd820f0 start_wqthread + 8
Hi,
We have been granted the com.apple.managed.vpn.shared entitlement and are able to use it for builds/TestFlight builds. We can access the cert in a mobile config. and everything works fine.
However when we try to archive a build and distribute for App Store Connect it fails if the entitlement file contains this entry. If we take it out the upload succeeds but the app can't load the cert from the keychain.
The Distribution profile has the entry:
keychain-access-groups: [TEAM ID].*, com.apple.managed.vpn.shared
Is there an extra step for App Store Connect builds?
Thanks,
Dave
My App is a rather small menu-bar status-item app with 2 informational windows. It does NOT make use of ANY of the APIs mentioned here: https://developer.apple.com/forums/thread/663874 that are bound to need "Local Network" hence trigger TCC dialog.
Yet - on first run of the app, the dialog pops.
App is Obj-C, and the only APIs used are Notification-Center (for scheduling local notifications to the user), XPC connections and calls to other (our) apps for gathering that information, plus normal AppKit windowing (Controls, Text-fields, etc.) nothing else.
Obviously SOMETHING I do causes the thing - and I know for sure this app DOES NOT NEED access to the local network - only I do not know how to identify the specific API I need to avoid using (or change the way I'm using)
Are there any specific system logs to watch for?
Is there any official set of APIs that will trigger the dialog?
Provided that I cannot avoid this - could this permission be granted via MDM profile payload? Our product comes with
I used the SSH approach method in the post https://developer.apple.com/forums/thread/703234 to add TLS trust for the local accessory device with a self signed certificate.
In the Info.plist, I disabled App Transport Security for local networking by setting the NSAllowsLocalNetworking property, as mentioned in the post.
However, I am still encountering the following SSL error:
ATS failed system trust
Connection 3: system TLS Trust evaluation failed(-9802)
Connection 3: TLS Trust encountered error 3:-9802
Connection 3: encountered error(3:-9802)
Task <9432C2C5-C7A1-44E4-95CC-2AFA49D6C501>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
Task <9432C2C5-C7A1-44E4-95CC-2AFA49D6C501>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3
In the code everything is working fine. The certificates are compared and
CFEqual(expected, actual), is returning true. Also in urlSession delegate method , the
return completionHandler(.useCredential, credential)
is returned.
When I disable ATS in Info.plist by setting NSAllowsArbitraryLoads, it works fine.
I have the following questions:
Should I disable ATS by setting NSAllowsArbitraryLoads along with setting ?
Instead of accepting the server certificate for the first time and saving it in the app, why can’t we embed the self-signed certificate in the app directly and use it for comparison?
When I was attempting to establish a TCP connection using NWConnection on a physical iPhone, the connection consistently fails with the error:
connection did change state, new: waiting(POSIXErrorCode(rawValue: 50): Network is down)
connection did change state, new: preparing
connection did change state, new: waiting(POSIXErrorCode(rawValue: 50): Network is down)
However, when running the same code in the iOS Simulator, the connection succeeds without issues.
I have already granted the app permission to access the local network by selecting "Allow [App Name] to find devices on the local network" when prompted.
The issue persists even after restarting the app, device, and server.
Additionally, I have configured the Info.plist file with the following keys:
NSAllowsLocalNetworking
NSLocalNetworkUsageDescription
Here is my code:
func start() -> NWConnection {
let myHost: NWEndpoint.Host = "192.168.1.154" // My MacBook
let myPort: NWEndpoint.Port = 1234
let connection = NWConnection(to: .hostPort(host: myHost, port: myPort), using: .tcp)
connection.stateUpdateHandler = { newState in
print("connection did change state, new: \(newState)")
}
connection.start(queue: .main)
return connection
}
Development environment:
Xcode 15.3
MacOS 14.4.1
Device OS: iOS 18.1
I'm attempting to use NWConnection as a websocket given a NWEndpoint returned by NWBrowser, setup like:
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.enableKeepalive = true
tcpOptions.keepaliveIdle = 2
let parameters = NWParameters(tls: nil, tcp: tcpOptions)
parameters.allowLocalEndpointReuse = true
parameters.includePeerToPeer = true
let options = NWProtocolWebSocket.Options()
options.autoReplyPing = true
options.skipHandshake = true
parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
self.connection = NWConnection(to: endpoint, using: parameters)
The initial connection does make it to the ready state but when I first try to send a text message over the websocket, i get
nw_read_request_report [C1] Receive failed with error "Input/output error"
nw_flow_prepare_output_frames Failing the write requests [5: Input/output error]
nw_write_request_report [C1] Send failed with error "Input/output error"
immediately, and the websocket is closed.
Send code here:
let encoder = JSONEncoder()
let dataMessage = try encoder.encode(myMessage)
let messageMetadata = NWProtocolWebSocket.Metadata(opcode: .text)
let context = NWConnection.ContentContext(identifier: "send", metadata: [messageMetadata])
connection.send(content: dataMessage, contentContext: context, completion: .contentProcessed({ error in
if let error = error {
print (error)
}
}))
What would typically cause the Input/output error when writing? Am I doing something obviously wrong or is there something else I can do to get additional debug information?
Thanks in advance for any help.
Hi,
I'm trying to set up automated backups on my machine using a combination of restic, a wrapper script, and a launchd agent, but I think I'm hitting a problem with the local network privacy dialogue.
Basically, the script sets up the environment variables for Restic, which then tries to backup to a local REST server. Problem is, when trying to do that, I get the following error:
Fatal: unable to open config file: Head "https://X:X@X.X.X.network:8000/X/X.X.X.network/config": dial tcp 192.168.50.229:8000: connect: no route to host
So it resolves DNS just fine, but can't connect to the local server. I tried a couple of things, tools such as ping work and can ping the local server, but nothing I do fixes the issue with restic itself. After reading about the network privacy feature, which I loved by the way, I believe it's the culprit here.
This is the .plist file I'm using, which lives in ~/Library/LaunchAgents/com.james.local-backup.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.james.local-backup</string>
<key>ProgramArguments</key>
<array>
<string>/Users/james/.local/bin/replicator</string>
<string>--backup</string>
<string>rest:https://X.X.X.network:8000/X/X.X.X.network</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/opt/coreutils/libexec/gnubin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>XDG_CONFIG_HOME</key>
<string>/Users/james/.config</string>
</dict>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>13</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardErrorPath</key>
<string>/tmp/com.user.backup.err</string>
<key>StandardOutPath</key>
<string>/tmp/com.user.backup.out</string>
<key>ProcessType</key>
<string>Background</string>
</dict>
</plist>
The local network dialogue never shows up, so I can't give the wrapper script or restic access to the local network, which I assume is why it can't connect to the local server.
Any way I can solve this? I could build a proper Swift CLI that calls restic, but I assume I'd hit the same issue. Plus, it seems overkill for my needs.
The content filtering functionality works perfectly when I install the app directly on my mobile device from Xcode. However, when the app is distributed via TestFlight, the app does not prompt users to save configurations, which causes the configuration-saving process to fail.
This app is meant for public use.
As per my understanding from TN3134, content filter apps can be shipped for devices running iOS 16 and later.
iOS app extension 9.0. supervised devices only
iOS app extension 15.0. apps using Screen Time API
iOS app extension 16.0. per app on managed devices
I am setting up a fake VPN with proxy settings using NEPacketTunnelProvider. When I check proxy check sites, I can see the proxy is detected.
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1")
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
proxySettings.httpsEnabled = true
proxySettings.httpServer = NEProxyServer(address: hostIP, port: portNumber)
proxySettings.httpsServer = NEProxyServer(address: hostIP2, port: portNumber2)
proxySettings.excludeSimpleHostnames = false
proxySettings.matchDomains = [""]
settings.proxySettings = proxySettings
How can I control whether other installed apps on the phone use or bypass this proxy? Can I do this with exceptionList? Since I am routing everything through a VPN, I assumed I could control this. The selection of which apps use the proxy should be up to the user.
Could you provide an explanation of how I can manage this? I am quite new to these types of tasks.
Hi,
I've read a bunch of threads regarding the changes in Sonoma and later requiring Location permission for receiving SSIDs. However, as far as I can see, in Sequoia 15.1 SSIDs and BSSIDs are empty regardless.
In particular, this makes it not possible to use associate(withName:) and associate(withSSID:) because the network object returned by scanForNetwork(withSSID: "...") has its .ssid and .bssid set to nil.
Here is an example:
First we have a wrapper to call the code after the location permission is authorized:
import Foundation
import CoreLocation
class LocationDelegate: NSObject, CLLocationManagerDelegate {
var onAuthorized: (() -> Void)?
var onDenied: (() -> Void)?
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let authStatus = manager.authorizationStatus
print("Location authorization status changed: \(authStatusToString(authStatus))")
if authStatus == .authorizedAlways {
onAuthorized?()
} else if authStatus == .denied || authStatus == .restricted {
onDenied?()
}
}
}
let locationManager = CLLocationManager()
let locationDelegate = LocationDelegate()
func authorizeLocation(onAuthorized: @escaping () -> Void, onDenied: @escaping () -> Void) {
let authStatus = locationManager.authorizationStatus
print("Location authorization status: \(authStatusToString(authStatus))")
if authStatus == .notDetermined {
print("Waiting for location authorization...")
locationDelegate.onAuthorized = onAuthorized
locationDelegate.onDenied = onDenied
locationManager.delegate = locationDelegate
locationManager.requestAlwaysAuthorization()
} else if authStatus == .authorizedAlways {
onAuthorized()
} else if authStatus == .denied || authStatus == .restricted {
onDenied()
}
RunLoop.main.run()
}
func authStatusToString(_ status: CLAuthorizationStatus) -> String {
switch status {
case .notDetermined:
return "Not Determined"
case .restricted:
return "Restricted"
case .denied:
return "Denied"
case .authorizedAlways:
return "Always Authorized"
case .authorizedWhenInUse:
return "Authorized When In Use"
@unknown default:
return "Unknown"
}
}
Then, a demo program itself:
import Foundation
import CoreWLAN
import Network
let client = CWWiFiClient.shared()
guard let interface = client.interface() else {
print("No wifi interface")
exit(1)
}
authorizeLocation(
onAuthorized: {
do {
print("Scanning for wifi networks...")
let scanResults = try interface.scanForNetworks(withSSID: nil)
let networks = scanResults.compactMap { network -> [String: Any]? in
return [
"ssid": network.ssid ?? "unknown",
"bssid": network.bssid ?? "unknown"
]
}
let jsonData = try JSONSerialization.data(withJSONObject: networks, options: .prettyPrinted)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
exit(0)
} catch {
print("Error: \(error)")
exit(1)
}
},
onDenied: {
print("Location access denied")
exit(1)
}
)
When launched, the program asks for permission, and after that, is shown as enabled in Privacy & Security Settings panel.
Here is the output where it can be seen that the scan is performed after location access was authorized, and regardless of that, all ssids are empty:
Location authorization status: Not Determined
Waiting for location authorization...
Location authorization status changed: Always Authorized
Scanning for wifi networks...
[
{
"ssid" : "unknown",
"bssid" : "unknown"
},
{
"ssid" : "unknown",
"bssid" : "unknown"
},
.... further omitted
Calling scanForNetworks() with explicitly specified network name does this as well, returns a CWNetwork object with .ssid / .bssid = nil.
I have a command line app under active development in XCode. It is based on receiving multicast traffic and processing it. I generate this traffic with another app, and generally just leave it running.
When I do a build and run in XCode, I get a message asking me for Local Access. If I click yes, no network traffic will be received. I need to restart the command line tool multiple times until I get access.
I'm also getting a ton of repeated entries in my Setting->Privacy->Local Access.
If I configure xcode to launch with terminal, it does work, but that's not a great solution because of the external window (and the fact that I have terminal set "close if exit cleanly", so I lose my data. I can change that setting, but it is fairly inconvenient, and I don't get the console history in XCode.
Is there a way to allow my apps to run from xcode without the pop-up or with the delay in activating the network and creating new entries in the Settings?
Thanks!
I’ve encountered an issue with an app that includes a Local Push Connectivity extension. After a fresh install of the app, the Local Network Alert appears when calling NEAppPushManager.save(). The alert message is:
“This app would like to find and connect to devices on your local network. This app will be able to discover and connect to devices on the networks you use.”
Here is the relevant code:
` pushManager.providerConfiguration = NEAppPushManager.providerConfiguration(with: settings, system: system)
if settings.ssids.isEmpty {
fatalError("☠️ The PushManagerSettings.ssids should NEVER be empty!")
}
pushManager.matchSSIDs = !settings.ssids.isEmpty
? Array(settings.ssids)
: []
return pushManager.save()`
Questions:
1. Why does the Local Network Alert appear?
I suspect it is related to pushManager.matchSSIDs, which interacts with the local network to match specific SSIDs.
2. What happens if the user clicks “Don’t Allow”?
Based on my testing, everything seems to work fine even if the user denies the permission.
Would you happen to know why this is happening and if denying the alert could cause any issues down the line?
Hi, all. We have a camera with only one WiFi module. It supports AP and STA modes coexisting, but the WiFi of AP and STA can only be in the same channel at the same time, that is, 2.4G or 5G. In the initial state, the App is connected to the camera through 5G WiFi, and the camera is in AP mode. When entering the network configuration mode, the camera will start the STA mode, and the AP and STA modes coexist. When the user selects 2.4G WiFi, the AP mode will switch from 5G to 2.4G. Android's WiFi and socket are not disconnected, iOS's socket will be disconnected 100%, and WiFi may be disconnected.
What is the reason for this? Is there any way to solve it?
Hi,
I'm adding a Content Filtering (FilterDataProvider) on macOS to an existing app and using MDM to avoid user interaction.
I start by pushing the following payloads to my machine:
com.apple.system-extension-policy
com.apple.webcontent-filter
And then installing notarized pkg containing my app and the NE.
Inspecting the system logs shows the following error:
neagent Failed to find a com.apple.networkextension.filter-data extension inside of app com.company_name.app_name.daemon
And calling
submit(request: .activationRequest(forExtensionWithIdentifier: bundleId, queue: queue))
results in:
Missing entitlement com.apple.developer.system-extension.install
Installing from Xcode on a SIP disabled machine works fine and both NE and CF are working as expected.
I followed the steps mentioned here https://developer.apple.com/forums/thread/737894 however the embedded entitlements already contained -systemextension suffix so I'm not sure if re signing and the subsequent steps are needed.
I also double checked that com.apple.developer.system-extension.install is present, certificates are not expired and that get-task-allow is not present in the embedded profile.
Here is what my release entitlement file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>content-filter-provider-systemextension</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>com.company_name.app_name.network-extension.content-filter</string>
</array>
</dict>
and my release app entitlement:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.endpoint-security.client</key>
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>content-filter-provider-systemextension</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
</dict>
</plist>
redacted logs
@eskimo may I ask for your help here!
Our company has a VPN client that we develop and it works on 14.x and it was working on 15.x but ever since I have upgraded to 15.1.1, I do not see any traffic being sent to the TUN interface even though I have it configured as the default route. Can anyone provide guidance or insight into what might have changed around the Network Extensions that could have caused this?
Unfortunately I cannot tell if this was happening on 15.0.1. Some things I have tried, to no avail, is disable the firewall and uninstalling/installing of the VPN client. I have no other filters installed that could be interfering. When I try and ping an address I should be able to reach, I get "no route to host"
I have also used Wireshark and have observed zero traffic going to the TUN interface.
NOTE, networking works fine when the VPN client is not connected.
Hi Team,
Looking for an answer, if it's just us or a widespread issue.
Since Sept, our clients Apple devices can't load a Captive Portal on Apple devices. Client wants the CNA to pop up and I can't get it to happen!
Android and Windows devices all work correctly with their respective popups, but CNA will not work.
No changes done on our side and after multiple tshoots and getting vendors to take multiple PCAPs found, Apple devices are not initiating a HTTP GET request as per Meraki >> https://documentation.meraki.com/MR/MR_Splash_Page/Splash_Page_Traffic_Flow_and_Troubleshooting
The work around is to force a HTTP GET request by manually going into the browser and initiate a http site (we tried 1.1.1.1, also tried other public HTTP sites and it works) and that redirects to our Captive Portal page.
We are trying to connect to Webdav.
The file server is in the same network.
So when we try to connect, the local network permission pop-up is displayed.
If the input information is incorrect in the first login attempt when this permission pop-up is displayed,
After that, even after fixing the normal connection, we cannot connect or log in with the message "NSURLErrorDomain Code=-1009", "Internet connection is offline."
This symptom seems to persist even after rebooting or deleting and deleting the app in the actual distributed app.
If you re-debug while debugging Xcode, you can connect normally.
(If you do not re-debug, it fails even if you enter the connection information normally.)
And it affects local connection, so you cannot connect to any local network server such as SMB or FTP.
Also, you cannot browse the server list within the local network. (SMB)
Is there a way to initialize the local network status within the app to improve this phenomenon?
I tried turning Airplane mode ON/OFF, turning Wi-Fi ON/OFF, and turning local network permissions ON/OFF, but it did not work.
Also, this phenomenon seems to be a Sandbox for each app.
When connecting to the same local server from an app installed on the same iPhone/iPad device, the above phenomenon does not occur if the first connection is successful.
** Summary **
If you fail to connect to a server on your local network,
then you will continue to fail to connect to the local server.
This happens even when local network permissions are allowed.
The error message is NSURLErrorDomain Code=-1009
The current device is an iPhone device running iOS 18.1.1.