appwrite / sdk-for-flutter

[READ-ONLY] Official Appwrite Flutter SDK πŸ’™

Home Page:https://appwrite.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

πŸ› Bug Report: Realtime.subscribe method doesn't dispatch any error if try to subscribe without connection.

FernandoUFS opened this issue Β· comments

πŸ‘Ÿ Reproduction steps

When using realtime.subscribe without connection it's not possible detect if it succeeded or failed, because the error of connection is not dispatched to outside of the sdk.

The try catch is never fired nor the stream.onError nor the stream.onDone when trying to subscribe.
Example:

try {
  RealtimeSubscription _messagesRealtimeSubscription = realtime.subscribe(['databases.chat.collections.${widget.group}.documents']);

  _chatSubscription = _messagesRealtimeSubscription.stream.listen((event) {
    Log.w(event.toString(), prefix: 'chat');
  }, onError: (e, st) {
    Log.e(e.toString(), st: st, prefix: 'chat');
  }, onDone: () {
    Log.e('Connection closed', prefix: 'chat');
  });
} catch (e, st) {
  Log.e(e, st: st, prefix: 'chat');
}

πŸ‘ Expected behavior

It should be possible to catch connection error and it should be possible to be sure that the connection was stablished.

When I say "to be sure that the connection was stablished" I mean that knowing if the stream had an error only solves one part of the problem. This is because I cannot be sure that the connection was successful simply by expecting not to receive an error.

Maybe the subscribe method being a Future I can know when the connection was stablished by awaiting.
For instance:

try {
    await realtime.subscribe(['databases.chat.collections.${widget.group}.documents']);
    log('success');
    showSuccessToUser();
} catch (e, st){
    log(e.toString);
    showErrorToUser();
    startAutoReconnectApproach();
}

πŸ‘Ž Actual Behavior

It actually never returns any errors in any way. The onDone function is only called if the connection was successfully established before and then the connection dies. However, if you try to subscribe without a connection, no error is dispatched.

🎲 Appwrite version

Version 2.0.x

πŸ’» Operating system

MacOS

🧱 Your Environment

flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[βœ“] Flutter (Channel stable, 3.7.6, on macOS 13.1 22C65 darwin-arm64, locale en-BR)
[βœ“] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[βœ“] Xcode - develop for iOS and macOS (Xcode 14.2)
[βœ“] Chrome - develop for the web
[βœ“] Android Studio (version 2022.1)
[βœ“] VS Code (version 1.76.0)
[βœ“] Connected device (2 available)
[βœ“] HTTP Host Availability

pubspec.yml

dependencies:
  appwrite: ^8.3.0

Device

Galaxy S22 with Android 13

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

  • I checked and didn't find similar issue

🏒 Have you read the Code of Conduct?

@FernandoUFS thanks for creating this issue! πŸ™πŸΌ I think this problem might be here:

Future.delayed(Duration.zero, () => _createSocket());

If _createSocket() throws an exception, it won't be handled.

@lohanidamodar, what are your thoughts on how to proceed with this?

@FernandoUFS thanks for creating this issue! πŸ™πŸΌ I think this problem might be here:

Future.delayed(Duration.zero, () => _createSocket());

If _createSocket() throws an exception, it won't be handled.

@lohanidamodar, what are your thoughts on how to proceed with this?

may be an await the Future can solve the problem?

@lohanidamodar I could solve this issue by two ways.

First way: The await worked, but to work I had to change the method subscribeTo from realtime_mixin.dart to return a Future<RealtimeSubscription> then, the same thing for Realtime.subscribe and its implementations. This causes a breaking change because the changing of the return type, everyone would have to do await realtime.subscribe now.
I committed the changes in a fork: FernandoUFS@a0825c8

Second way: This line is the one causing the error when trying connecting without connection:

_websok = await getWebSocket(uri);

Using a try catch in that point we can send the error to the streams. But also it would be necessary send a success stream, so we can be sure that the connection was stablished.

I tested with this code, and I could get the error in the realtimeSubscription stream onError.

try {
      if (_websok == null) {
        _websok = await getWebSocket(uri);
        // Create a RealtimeMessage and send to the channels informing that the connection was success.
        _lastUrl = uri.toString();
      } else {
        if (_lastUrl == uri.toString() && _websok?.closeCode == null) {
          return;
        }
        await _closeConnection();
        _lastUrl = uri.toString();
        _websok = await getWebSocket(uri);
      }
    } catch (e) {
      for (var list in _channels.values) {
        for (var stream in list) {
          stream.sink.addError(AppwriteException('Error connecting to server'));
        }
      }
    }

Thank you @FernandoUFS would you like to make a PR against https://github.com/appwrite/sdk-generator ? If not, no worries, we will look into it as well.

Hi @lohanidamodar, as you can see, I was able to solve the problem in two ways, but I think the first way (the code in the repository) is more intuitive. However, this causes a breaking change, because now everyone will need to use await realtime.subscribe for the code to work. So, I would prefer you analyze which is the best option. Thank you.

Thank you @FernandoUFS yes, we would not want to break the interface as well as would want to keep it consistent with other SDKs. So giving back the error on onError for streams is the better option