Matthias Nott
2026-03-24 547ee7cd0045d5604d143733d37a3d02f52ad1b8
lib/services/mqtt_service.dart
....@@ -514,11 +514,25 @@
514514 void didChangeAppLifecycleState(AppLifecycleState state) {
515515 switch (state) {
516516 case AppLifecycleState.resumed:
517
- if (_status != ConnectionStatus.connected && !_intentionalClose) {
517
+ if (_intentionalClose) break;
518
+ // iOS kills the TCP socket during suspend. The MQTT client may still
519
+ // think it's connected until the next ping timeout (30s+). Force a
520
+ // fresh connection to avoid the "dead but looks alive" state.
521
+ final client = _client;
522
+ if (client != null && client.connectionStatus?.state == MqttConnectionState.connected) {
523
+ // Verify the connection is actually alive by checking disconnect state
524
+ // If auto-reconnect already fired, this is a no-op
525
+ _mqttLog('MQTT: app resumed, verifying connection...');
526
+ // Force disconnect + reconnect to get a clean state
527
+ client.autoReconnect = false;
528
+ client.disconnect();
529
+ _client = null;
530
+ _setStatus(ConnectionStatus.reconnecting);
531
+ Future.delayed(const Duration(milliseconds: 500), () => connect());
532
+ } else if (_status != ConnectionStatus.connected) {
518533 connect();
519534 }
520535 case AppLifecycleState.paused:
521
- // Keep connection alive — MQTT handles keepalive natively
522536 break;
523537 default:
524538 break;