NWConnection Always Fails on Physical iPhone but Works on Simulator

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
Answered by DTS Engineer in 817221022

Agreed.

On that thread I outlined the steps for filing a bug about this if you manage to reproduce it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

If you run an RVI packet trace while attempting to connect, do you see any traffic on the ‘wire’?

NSAllowsLocalNetworking

FYI, that’s an App Transport Security (ATS) key and ATS only applies to URLSession and higher. It’s ignored by low-level APIs like Network framework.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

After removing NSAllowsLocalNetworking, I deleted and reinstalled the app on my iPhone, and it was able to successfully access the server and return data. However, without changing the code, if I delete and reinstall the app again, it reverts to the previous issue of being unable to connect.

At this point, if I change 192.168.1.154 to Erics-MacBook-Pro.local, the connection works again. But after deleting and reinstalling the app without making any code changes, it reverts to the same issue of being unable to connect.

It seems like the app can successfully connect on the first installation after modifying some settings.

I set up a new RVI for the iPhone and recorded a trace. During the trace, I issued a request from the device and then completed the trace, generating a pcap file. However, upon analyzing the pcap file, I couldn’t find any traffic related to IP 192.168.1.154 or port 1234. This indicates that there was no traffic on the ‘wire’.

Here’s a recap of the process:

  1. Upon the first installation of the app, executing NWConnection.start() triggers the prompt: “Allow [App Name] to find devices on the local network”.
  2. After selecting “Allow,” the app receives the error: waiting(POSIXErrorCode(rawValue: 50): Network is down).
  3. Using an RVI packet trace reveals no traffic from the iPhone.
  4. Restarting the app, device, and server yields the same result.
  5. Clean project, build and run the app without modifying any code also yields the same result.
  6. Adding a new line in a comment (no functional code changes), build and run the app allows it to connect successfully.
  7. Restarting the app, device, and server after this also works, and the app continues to connect successfully.
  8. Using an RVI packet trace after this shows data being exchanged between 192.168.1.154 and 192.168.1.157.

This is my first time working with the Network Framework, any hints or suggestions would be appreciated!

This is quite mysterious. I do this sort of thing all the time without any problems.

Lemme start by setting a baseline:

  1. I’m testing with Xcode 16.1 running on macOS 14.7.1 targeting iOS 18.1. My Mac and iOS device are on the same Wi-Fi.

  2. I created a new test project from the iOS > App template and wired up a button to the startStop() method of the code below. 192.168.1.171 is the IP address of my Mac.

  3. On the Mac I ran a dummy server like so:

    % while true ; do date ; nc -l 12345 ; done
    
  4. I ran the app on the device from Xcode and tapped the Stop/Stop button.

  5. The system presented the Local Network alert. I authorised the access. The connection then went through. In Xcode I saw this:

    connection will start
    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)
    connection did change state, new: preparing
    connection did change state, new: ready
    

    In Terminal I saw this:

    % while true ; do date ; nc -l 12345 ; done
    Fri  6 Dec 2024 12:12:09 GMT
    Hello Cruel World! 2024-12-06 12:12:14 +0000
    
  6. I played around with various scenarios — building and running again, deleting the app, tweaking the code in irrelevant ways — and everything continued to work as expected.

I suspect there’s some sort of environmental thing in play. Earlier you wrote:

Development environment: Xcode 15.3 … macOS 14.4.1 … iOS 18.1

Is that accurate? Because Xcode 15 doesn’t support iOS 18.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

class Core: ObservableObject {

    var connectionQ: NWConnection? = nil
    
    func start() -> NWConnection {
        print("connection will start")
        let connection = NWConnection(to: .hostPort(host: "192.168.1.171", port: 12345), using: .tcp)
        connection.stateUpdateHandler = { newState in
            print("connection did change state, new: \(newState)")
        }
        let message = "Hello Cruel World! \(Date())\r\n"
        connection.send(content: Data(message.utf8), completion: .idempotent)
        connection.start(queue: .main)
        return connection
    }
    
    func stop(connection: NWConnection) {
        print("connection will stop")
        connection.stateUpdateHandler = nil
        connection.cancel()
    }
    
    func startStop() {
        if let connection = self.connectionQ {
            self.connectionQ = nil
            self.stop(connection: connection)
        } else {
            self.connectionQ = self.start()
        }
    }
}

Thank you for your response. I tried your code, but it still returned the following log:

connection will start
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)

My iPhone device OS version is 18.1, but the iOS Deployment Target in Xcode is set to 17.4. This might be the issue. I will upgrade my system and Xcode version and try again.

I have updated my development environment, but the issue remains the same.

Development environment:

  • macOS: 14.7.1
  • Xcode: 16.1
  • Deployment Target: iOS 18.1
  • iPhone Device OS: iOS 18.1

I also conducted RVI traces, and they show that no traffic is being sent from the iPhone. Do you have any additional suggestions on how to debug this issue?

Besides modifying the code and performing a build & run, restarting the iPhone device also allows the connection to succeed.

I believe this issue aligns with the one discussed in this thread: 768666

Accepted Answer

Agreed.

On that thread I outlined the steps for filing a bug about this if you manage to reproduce it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

NWConnection Always Fails on Physical iPhone but Works on Simulator
 
 
Q