Hello,
We have a SwiftUI-based application that runs as a LaunchAgent and communicates with other internal components using Unix domain sockets (UDS).
On Sequoia (macOS virtualized environment), when installing the app, we encounter the Local Network Privacy Alert, asking: "Allow [AppName] to find and connect to devices on the local network?"
We are not using any actual network communication — only interprocess communication via UDS.
Is there a way to prevent this system prompt, either through MDM configuration or by adjusting our socket-related implementation?
Here's a brief look at our Swift/NIO usage:
class ClientHandler: ChannelInboundHandler {
...
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
...
}
...
}
// init bootstrap.
var bootstrap: ClientBootstrap {
return ClientBootstrap(group: group)
// Also tried to remove the .so_reuseaddr, the prompt was still there.
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
// Add ChannelInboundHandler reader.
channel.pipeline.addHandler(ClientHandler())
}
}
// connect to the UDS.
self.bootstrap.connect(unixDomainSocketPath: self.path).whenSuccess { (channel) in
..
self.channel = channel
}
...
...
// Send some data.
self.channel?.writeAndFlush(buffer).wait()
Any guidance would be greatly appreciated.
For lots of background on local network privacy, see TN3179 Understanding local network privacy.
Unix domain sockets are not one of the operations that requires local access. I’m not sure what’s going on with your program but I just ran a quick test here and confirmed that conclusion. Specifically, I ran the code below from within Xcode 16.3 on macOS 15.4 and it was able to connect without showing the local network alert.
I encourage you to replicate that test in your environment.
IMPORTANT Run the program from Xcode (Product > Run) rather than Terminal. TN3179 explains that Terminal, and the tools it spawns, aren’t subject to local network privacy.
Once you’ve confirmed that Unix domains sockets isn’t the culprit here, you’ll need to hunt for the code that is. Unfortunately there’s no easy way to do that. TN3179 discusses the options you do have in its Unexpected local network alert section.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
import Foundation
import Network
func startReceive(connection: NWConnection, connectionID: Int) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 1024) { content, contentContext, isComplete, error in
if let content {
print("connection \(connectionID) did receive content, count: \(content.count)")
}
if isComplete {
print("connection \(connectionID) did receive EOF")
}
if let error {
print("connection \(connectionID) did receive failure, error: \(error)")
} else {
startReceive(connection: connection, connectionID: connectionID)
}
}
}
func startConnection(_ connection: NWConnection, connectionID: Int) {
connection.stateUpdateHandler = { newState in
print("connection \(connectionID) did change state, new: \(newState)")
}
startReceive(connection: connection, connectionID: connectionID)
connection.start(queue: .main)
}
var connections: [NWConnection] = []
func startListener(socketSite: URL) throws -> NWListener {
let parameters = NWParameters.tcp
parameters.requiredLocalEndpoint = .unix(path: socketSite.path)
let listener = try NWListener(using: parameters)
listener.stateUpdateHandler = { newState in
print("listener did change state, new: \(newState)")
}
listener.newConnectionHandler = { connection in
let connectionID = connections.count
print("listener did receive connection, id: \(connectionID)")
connections.append(connection)
startConnection(connection, connectionID: connectionID)
}
listener.start(queue: .main)
return listener
}
func startClient(socketSite: URL) {
print("client will start, id: -1")
let connection = NWConnection(to: .unix(path: socketSite.path), using: .tcp)
let message = "Hello Cruel World! \(Date.now)"
print("client will send, message: '\(message)'")
connection.send(content: Data((message + "\r\n").utf8), completion: .contentProcessed({ error in
if let error {
print("client did not send, error: '\(error)'")
} else {
print("client did send")
}
}))
startConnection(connection, connectionID: -1)
}
func main() throws {
let socketSite = URL(fileURLWithPath: "/Users/quinn/Test/test.sock")
try? FileManager.default.removeItem(at: socketSite)
let listener = try startListener(socketSite: socketSite)
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
startClient(socketSite: socketSite)
}
withExtendedLifetime(listener) {
dispatchMain()
}
}
try main()