From 3042f50a251f40696ede7a215fd777fe81ebb19c Mon Sep 17 00:00:00 2001 From: dimakis Date: Fri, 5 Jun 2026 08:16:05 +0100 Subject: [PATCH] fix(client): always force WS reconnect on iOS foreground return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS kills WebSocket connections during background without updating readyState. checkAndReconnect() previously trusted readyState, so if the socket appeared OPEN the function did nothing — first message sent on the dead socket was silently lost. Remove the readyState guard: always tear down the old socket and create a fresh connection. Sends during the reconnect window queue in pendingSends and flush after the welcome handshake completes. Co-Authored-By: Claude Opus 4.6 --- packages/client/src/connection.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/client/src/connection.ts b/packages/client/src/connection.ts index 21be03a1..48fba677 100644 --- a/packages/client/src/connection.ts +++ b/packages/client/src/connection.ts @@ -324,14 +324,21 @@ export class MitzoConnection { } } - /** Force a reconnect check — call from native lifecycle hooks (e.g. Capacitor appStateChange). */ + /** + * Force a reconnect — call from native lifecycle hooks (e.g. Capacitor + * appStateChange) and browser visibility events. + * + * iOS kills WebSocket connections during background without updating + * readyState, so we NEVER trust the old socket after a foreground + * transition. Always tear down and create a fresh connection. Any + * sends that arrive during the reconnect window queue in pendingSends + * and flush after the welcome handshake completes. + */ checkAndReconnect(): void { - if (!this.ws || this.ws.readyState !== WS_READY_STATE.OPEN) { - if (this.reconnectTimer) return; - this.defuseOldWs(); - this._connected = false; - this.listener?.({ type: '_close' }); - this.doConnect(); - } + if (this.reconnectTimer) return; + this.defuseOldWs(); + this._connected = false; + this.listener?.({ type: '_close' }); + this.doConnect(); } }