appwrite / sdk-for-flutter

[READ-ONLY] Official Appwrite Flutter SDK πŸ’™
https://appwrite.io
BSD 3-Clause "New" or "Revised" License
365 stars 111 forks source link

πŸ› Bug Report: Realtime does not reconnect when connection is lost #108

Open merabtenei opened 1 year ago

merabtenei commented 1 year ago

πŸ‘Ÿ Reproduction steps

I'm using Flutter and below is a very simple app that will reproduce this behavior.

import 'package:appwrite/appwrite.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appwrite Realtime Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Appwrite Realtime Test'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Client client = Client();

  late RealtimeSubscription subscription;

  @override
  void initState() {
    client
            .setEndpoint(
                'https://xxxxxxxx.xxx/v1') // Your Appwrite Endpoint
            .setProject('xxxxx') // Your project ID
            .setSelfSigned() // Use only on dev mode with a self-signed SSL cert
        ;
    Realtime realtime = Realtime(client);
    subscription =
        realtime.subscribe(['databases.xxxxx.collections.xxxxx.documents']);
    subscription.stream.listen((event) => print('Received event'),
        onDone: () => print('DONE'), onError: (error, st) => print('ERROR'));

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: StreamBuilder(
          stream: subscription.stream,
          builder: (context, snapshot) {
            if (snapshot.hasData && snapshot.data != null) {
              return Text(snapshot.data!.payload.toString());
            } else if (snapshot.hasError) {
              return Tooltip(
                message: snapshot.error.toString(),
                child: const Icon(Icons.error),
              );
            } else {
              return const CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

πŸ‘ Expected behavior

One of two scenarios :

πŸ‘Ž Actual Behavior

🎲 Appwrite version

Version 1.1.x

πŸ’» Operating system

Linux

🧱 Your Environment

No response

πŸ‘€ Have you spent some time to check if this issue has been raised before?

🏒 Have you read the Code of Conduct?

stnguyen90 commented 1 year ago

@lohanidamodar, would you be able to check on this?

merabtenei commented 1 year ago

@lohanidamodar, would you be able to check on this?

Just found something: I cloned the flutter Sdk and managed to detect when the Websocket gets closed : In the file realtime_mixin.dart

_websok?.stream.listen(
        (response) {
          ...
        },
        // Added these callbacks
        onError: (err, st) =>
            debugPrint('realtime_mixin:onError: ${err.toString()}'),
        onDone: () => debugPrint('realtime_mixin:onDone'),
      );

I added onError and onDone callback and now I can see realtime_mixin:onDone on the console, I think from here we need to do something to receive the closing event. As of now onDone or onError of the Stream provided by the subscribe method are not triggered. It's the first time I take a look inside the Sdk code, so I'm not sure if that's all what it takes to fix it.

lohanidamodar commented 1 year ago

@merabtenei Thanks a lot on identifying the issue, I'll look into it and come back to you.

merabtenei commented 1 year ago

I have an addition to this topic. Executing a realtime subscription in a background or foreground service is terminated a few seconds after a screen lock or app going to background. Using this package could help : https://pub.dev/documentation/websocket_manager/latest/

This plugin was created due to our necessity to maintain a WebSocket connection active in background while Flutter's WebSocket from cookbook doesn't keep alive while screen is locked or the application was in background.

merabtenei commented 1 year ago

Is there any update regarding this one ? Is there any safe way to detect if the subscription connection was lost due to the device loosing access to internet ?

lohanidamodar commented 1 year ago

@merabtenei we cannot use websocket manager as it supports only android and iOS. I'm looking it getting a message back so that we will know when the connection was closed.

lohanidamodar commented 1 year ago

@merabtenei can you check if you get back the message on subscription.stream.onDone when websocket closes with this changes? https://github.com/appwrite/sdk-for-flutter/tree/fix-websocket-issue

merabtenei commented 1 year ago

@merabtenei can you check if you get back the message on subscription.stream.onDone when websocket closes with this changes? https://github.com/appwrite/sdk-for-flutter/tree/fix-websocket-issue

I confirm that onDone callback is being correctly triggered now. Good job.

stnguyen90 commented 1 year ago

Fixed as part of 8.2.2: https://github.com/appwrite/sdk-for-flutter/blob/6fecb9b56ac023133b0944cc5f14d071412dafd1/lib/src/realtime_mixin.dart#L82

stnguyen90 commented 3 weeks ago

Re-opening as I think we need to add reconnection logic like how the apple and android SDKs have.