From 547ee7cd0045d5604d143733d37a3d02f52ad1b8 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Tue, 24 Mar 2026 02:11:51 +0100
Subject: [PATCH] fix: force MQTT reconnect on app resume to avoid stale connections

---
 lib/services/mqtt_service.dart |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index f7a51be..35464de 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -514,11 +514,25 @@
   void didChangeAppLifecycleState(AppLifecycleState state) {
     switch (state) {
       case AppLifecycleState.resumed:
-        if (_status != ConnectionStatus.connected && !_intentionalClose) {
+        if (_intentionalClose) break;
+        // iOS kills the TCP socket during suspend. The MQTT client may still
+        // think it's connected until the next ping timeout (30s+). Force a
+        // fresh connection to avoid the "dead but looks alive" state.
+        final client = _client;
+        if (client != null && client.connectionStatus?.state == MqttConnectionState.connected) {
+          // Verify the connection is actually alive by checking disconnect state
+          // If auto-reconnect already fired, this is a no-op
+          _mqttLog('MQTT: app resumed, verifying connection...');
+          // Force disconnect + reconnect to get a clean state
+          client.autoReconnect = false;
+          client.disconnect();
+          _client = null;
+          _setStatus(ConnectionStatus.reconnecting);
+          Future.delayed(const Duration(milliseconds: 500), () => connect());
+        } else if (_status != ConnectionStatus.connected) {
           connect();
         }
       case AppLifecycleState.paused:
-        // Keep connection alive — MQTT handles keepalive natively
         break;
       default:
         break;

--
Gitblit v1.3.1