| .. | .. |
|---|
| 514 | 514 | void didChangeAppLifecycleState(AppLifecycleState state) { |
|---|
| 515 | 515 | switch (state) { |
|---|
| 516 | 516 | 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) { |
|---|
| 518 | 533 | connect(); |
|---|
| 519 | 534 | } |
|---|
| 520 | 535 | case AppLifecycleState.paused: |
|---|
| 521 | | - // Keep connection alive — MQTT handles keepalive natively |
|---|
| 522 | 536 | break; |
|---|
| 523 | 537 | default: |
|---|
| 524 | 538 | break; |
|---|