WebSocket does not behave correctly on iOS, not switching state correctly on iOS, not visibly losing connection on wifi switch, but resetting server connection
Describe the bug
Hey, I noticed that the WebSocket does not seem to behave correctly on iOS under certain circumstances. We get data messages regularly back to the client, but when messing with the wifi, the messages wont be received anymore. Also the state still is open and does not change at all.
On Android, the state will change to disconnected/reconnecting and after reopening the app, because we listen to the state change, we can reconnect accordingly. Because we do not ever notice this on iOS, we cannot fix the connection.
To Reproduce Steps to reproduce the behavior:
connection code:
const intervalOptions = (
min: Duration(seconds: 5 - 2),
max: Duration(seconds: 5 + 2),
);
const timeout = Duration(seconds: 10);
WebSocketClient client;
if ((kIsWeb)) {
client = WebSocketClient(WebSocketOptions.js(
connectionRetryInterval: intervalOptions, timeout: timeout));
} else {
client = WebSocketClient(WebSocketOptions.vm(
connectionRetryInterval: intervalOptions, timeout: timeout));
}
client.stream.listen(
_onMessage,
_onError,
_onDisconnect
);
client.stateChanges.listen((state) async {
print("ws state change $state id:${identityHashCode(this)}");
switch (state) {
case WebSocketClientState$Connecting():
print("ws connecting");
break;
case WebSocketClientState$Open():
await _onConnect(client);
break;
case WebSocketClientState$Disconnecting():
case WebSocketClientState$Closed():
state as WebSocketClientState$Disconnecting;
print(
"ws close, code: \"${state.closeCode}\", reason: \"${state.closeReason}\"");
_onDisconnect();
break;
}
});
client.connect(url);
_client = client;
In _onConnect(client) we will send a message to the server to subscribe to data messages, which then come in like they should, at first.
- swipe down iOS Control Center
- turn off wifi
- turn on wifi
- open WiFi Settings/long tap wifi icon
- switch to another WiFi (which also has internet connection, we do not have a cellular connection, btw.)
- go back to the app
- notice in the app or in the log that websocket messages are not coming in anymore, but state did not change
With this, i can replicate the behavior 95% of the time. When only toggling wifi shortly or only switching wifi network, the issue also comes up from time to time, but less often. We have iOS users reporting that they do not receive updates after some time, where I suspect that network changes are the cause.
Expected behavior I would expect the behavior to be like on Android, where it does change state and I can detect a change in connection, eventually also re-connect, and the app being able to re-subscribe.
~~Screenshots~~
Smartphones:
- not working:
- Device: iPhone 16e
- OS: iOS 26.0.1
- Library-Version 1.0.0
- working:
- Device: Google Pixel 6
- OS: Android 16
- Library-Version 1.0.0
You should implement custom ping/pong or heartbeat messages. The logic is that you should send a ping (heartbeat) message to the client from the backend every 30 seconds. If the client doesn't receive any message within 35 seconds, it disconnects.
We already did this as a workaround, but it would still be nice to not have to implement such a thing, because it is dead code basically :)
It's not a thing with priority, but I just wanted to bring it up and I would like if it was fixed at some point
@EtzBetz, sadly, that's not possible. WebSockets doesn't handle that case or pings. But if you have some suggestion or idea - fill free to share with me.
I'll try to come up with something myself for the next major update. When the Native Assets feature becomes available for Dart. I have a concept that could possibly make WebSockets better.
https://dart.dev/tools/hooks