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
This suggests that the issue reported for iOS 18 may still be present in iOS 26.
Hmmmm, no. When we investigated veerasivaraman’s primary issue, FB18502752, we determined that the local network privacy prompt wasn’t showing because the address they’re accessing wasn’t on a local network.
Which is representative of my general experience with local network privacy on modern versions of iOS. It seems to work pretty much as explained in TN3179 Understanding local network privacy.
IMPORTANT This isn’t to say that I think local network privacy works perfectly. We continue to find problems with it, especially on macOS. However, the majority of problem reports I see from iOS are misunderstandings of how it actually works.
Regarding step 3, does your accessory contain a DHCP server? If so, does its DHCP response list the accessory’s IP address as a DNS server. If so, local network privacy doesn’t apply. Quoting TN3179:
Note these exceptions to the rules above:
- If your device’s DNS server is on a local network, traffic to it doesn’t require local network access.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"