Is There a Reliable Way to Check Local Network Permission Status in 2025?
I've read many similar requests, but I'm posting this in 2025 to ask:
Is there any official or reliable method to check the current Local Network permission status on iOS 18.x?
We need this to guide or navigate users to the appropriate Settings page when permission is denied.
Background
Our app is an IoT companion app, and Local Network access is core to our product's functionality. Without this permission, our app cannot communicate with the IoT hardware. Sadly, Apple doesn't provide any official API to check the current status of this permission.
This limitation has caused confusion for many users, and we frequently receive bug reports simply because users have accidentally denied the permission and the app can no longer function as expected.
Our App High Level Flow:
1. Trigger Permission
We attempt to trigger the Local Network permission using Bonjour discovery and browsing methods. (see the implementation)
Since there's no direct API to request this permission, we understand that iOS will automatically prompt the user when the app makes its first actual attempt to communicate with a local network device.
However, in our case, this creates a problem:
The permission prompt appears only at the time of the first real connection attempt (e.g., when sending an HTTP request to the IoT device).
This results in a poor user experience, as the request begins before the permission is granted.
The first request fails silently in the background while the permission popup appears unexpectedly.
We cannot wait for the user's response to proceed, which leads to unreliable behavior and confusing flows.
To avoid this issue, we trigger the Local Network permission proactively using Bonjour-based discovery methods. This ensures that the system permission prompt appears before any critical communication with the IoT device occurs.
We’ve tried alternative approaches like sending dummy requests, but they were not reliable or consistent across devices or iOS versions. (see the support ticket)
2. Wi-Fi Connection:
Once permission is granted, we allow the user to connect to the IoT device’s local Wi-Fi.
3. IoT Device Configuration:
After connecting, we send an HTTP request to a known static IP (e.g., 192.168.4.1) on the IoT network to configure the hardware.
I assume this pattern is common among all Wi-Fi-based IoT devices and apps.
Problem:
Even though we present clear app-level instructions when the system prompt appears, some users accidentally deny the Local Network permission. In those cases, there’s no API to check if the permission was denied, so:
We can’t display a helpful message.
We can’t guide the user to Settings → Privacy & Security → Local Network to re-enable it.
The app fails silently or behaves unpredictably.
Developer Needs:
As app developers, we want to handle negative cases gracefully by:
Detecting if the Local Network permission was denied
Showing a relevant message or a prompt to go to Settings
Preventing silent failures and improving UX
So the question is:
What is the current, official, or recommended way to determine whether Local Network permission is granted or denied in iOS 18.x (as of 2025)?
This permission is critical for a huge category of apps especially IoT and local communication-based products. We hope Apple will offer a better developer experience around this soon.
Thanks in advance to anyone who can share updated guidance.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
We are developing an IoT companion app that connects to the IoT device's Wi-Fi network and communicates with it through local network APIs.
To support this functionality, we have:
Added the necessary keys in the Info.plist.
NSLocalNetworkUsageDescription ,
NSBonjourServices
Used a Bonjour service at app launch to trigger the local network permission prompt.
Problem on iOS 18.x (including 18.6 beta)
Even when the user explicitly denies the local network permission, our API communication still works.
This is unexpected behavior, as we assume denying permission should restrict access to local network communication.
We tested this with the latest iOS 18.6 beta (as per Thread 789461021), but the issue still persists.
This behavior raises concerns about inconsistent permission enforcement in iOS 18.x.
Problem on iOS 17.x
In iOS 17.x, if the user accidentally denies the local network permission and later enables it manually via Settings, the change does not take effect immediately.
The app cannot access the local network unless the device is restarted, which results in a confusing and poor user experience.
Expected Behavior
If local network permission is denied, local API communication should be strictly blocked.
If the permission is later enabled via Settings, the app should regain access without requiring a device restart.
Request
We request clarification and resolution on:
Why local network APIs are accessible even when permission is denied on iOS 18.x.
Whether the delayed permission update (requiring restart) in iOS 17.x is expected or a known issue.
Best practices to ensure consistent and predictable permission handling across iOS versions.
I've a iOT companion app, in which I'll connect to iOT's Wi-Fi and then communicate the device with APIs,
for the above functionality we needed local network permission So we enabled neccessary keys in info.plist and at the time of App Launch we trigger local network permission using the following code
info.plist
<string>This app needs local network access permission to connect with your iOT device and customize its settings</string>
<key>NSBonjourServices</key>
<array>
<string>_network-perm._tcp</string>
<string>_network-perm._udp</string>
</array>
Network Permission Trigger Methods
import Foundation
import MultipeerConnectivity
class NetworkPermissionManager: NSObject {
static let shared = NetworkPermissionManager()
private var session: MCSession?
private var advertiser: MCNearbyServiceAdvertiser?
private var browser: MCNearbyServiceBrowser?
private var permissionCallback: ((String) -> Void)?
func requestPermission(callback: @escaping (String) -> Void) {
self.permissionCallback = callback
do {
let peerId = MCPeerID(displayName: UUID().uuidString)
session = MCSession(peer: peerId, securityIdentity: nil, encryptionPreference: .required)
session?.delegate = self
advertiser = MCNearbyServiceAdvertiser(
peer: peerId,
discoveryInfo: nil,
serviceType: "network-perm"
)
advertiser?.delegate = self
browser = MCNearbyServiceBrowser(
peer: peerId,
serviceType: "network-perm"
)
browser?.delegate = self
advertiser?.startAdvertisingPeer()
browser?.startBrowsingForPeers()
// Stop after delay
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
self?.stopAll()
// If no error occurred until now, consider permission triggered
self?.permissionCallback?("granted")
self?.permissionCallback = nil
}
} catch {
permissionCallback?("error: \(error.localizedDescription)")
permissionCallback = nil
}
}
func stopAll() {
advertiser?.stopAdvertisingPeer()
browser?.stopBrowsingForPeers()
session?.disconnect()
}
}
extension NetworkPermissionManager: MCSessionDelegate {
func session(_: MCSession, peer _: MCPeerID, didChange _: MCSessionState) {}
func session(_: MCSession, didReceive _: Data, fromPeer _: MCPeerID) {}
func session(_: MCSession, didReceive _: InputStream, withName _: String, fromPeer _: MCPeerID) {}
func session(_: MCSession, didStartReceivingResourceWithName _: String, fromPeer _: MCPeerID, with _: Progress) {}
func session(_: MCSession, didFinishReceivingResourceWithName _: String, fromPeer _: MCPeerID, at _: URL?, withError _: Error?) {}
}
extension NetworkPermissionManager: MCNearbyServiceAdvertiserDelegate {
func advertiser(_: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer _: MCPeerID, withContext _: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
invitationHandler(false, nil)
}
func advertiser(_: MCNearbyServiceAdvertiser, didNotStartAdvertisingPeer error: Error) {
print("❌ Advertising failed: \(error)")
if let nsError = error as NSError?, nsError.domain == NetService.errorDomain, nsError.code == -72008 {
permissionCallback?("denied")
} else {
permissionCallback?("error: \(error.localizedDescription)")
}
permissionCallback = nil
stopAll()
}
}
extension NetworkPermissionManager: MCNearbyServiceBrowserDelegate {
func browser(_: MCNearbyServiceBrowser, foundPeer _: MCPeerID, withDiscoveryInfo _: [String: String]?) {}
func browser(_: MCNearbyServiceBrowser, lostPeer _: MCPeerID) {}
func browser(_: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: Error) {
print("❌ Browsing failed: \(error)")
if let nsError = error as NSError?, nsError.domain == NetService.errorDomain, nsError.code == -72008 {
permissionCallback?("denied")
} else {
permissionCallback?("error: \(error.localizedDescription)")
}
permissionCallback = nil
stopAll()
}
}```
I want to satisfy this following cases but it's not working as expected
# Case1 Working
App launches --> trigger permission using above code --> user granted permission --> connect to iOT's Wi-Fi using app --> Communicate via Local API ---> should return success response
# Case2 Not working
App launches --> trigger permission using above code --> user denied permission --> connect to iOT's Wi-Fi using app --> Communicate via Local API ---> should throw an error
I double checked the permission status in the app settings there also showing disabled state
In my case case 2 is also return success, even though user denied the permission I got success response. I wonder why this happens
the same above 2 cases working as expected in iOS 17.x versions