pubnub / dart

PubNub Dart SDK
Other
28 stars 15 forks source link

How do we listen for PubNub connectivity/status events? #97

Open tfrysinger opened 2 years ago

tfrysinger commented 2 years ago

We have successfully implemented PubNub using the Java SDK, and are now porting our app to Flutter using the Dart SDK using the latest 4.1.3. I am not seeing the equivalent of the status() method in the SubscribeCallback object that is in the Java SDK in the Dart SDK. I notice that the listeners section for a subscription describes multiple types, but none of them are specific to status.

Is there a separate stream we are supposed to listen on to receive status events for PubNub as a whole such as connect, disconnect, reconnect, etc? The Java SDK has a very robust set of events that are sent.

Our app has a separate module that tracks catching connectivity changes for the Internet, but using the Java SDK we noticed many times the Internet connection would be healthy but for whatever reason the connection to PubNub would be temporarily disconnected, then automatically reconnected according to our retry policy. It would send notifications for each state that our app responds to (disabling some areas, visually showing the user that they can send messages to other subscribers, etc.)

Here is the sample code we are using just to test connectivity:

      GlobalUtils.globalLogger.info("connectToService() called, using channel $statusChannel");
      statusSubscription =  pubNub?.subscribe(channels: {statusChannel ?? 'statusChannel'});
      statusSubscription?.messages.listen((envelope) {
        GlobalUtils.globalLogger.info("Envelope received");
        switch (envelope.messageType) {
          case MessageType.normal:
            GlobalUtils.globalLogger.info('A normal message was received: ${envelope.content}');
            break;
          case MessageType.signal:
            GlobalUtils.globalLogger.info('A signal message was received: ${envelope.content}');
            break;
          case MessageType.objects:
            GlobalUtils.globalLogger.info('An object event was received with data ${envelope.payload['data']}');
            break;
          case MessageType.messageAction:
            GlobalUtils.globalLogger.info('A action message ${envelope.payload['event']} was received with data ${envelope.payload['data']}');
            break;
          case MessageType.file:
            var fileInfo = envelope.payload['file'];
            var id = fileInfo['id']; // unique file id
            var name = fileInfo['name']; // file name
            GlobalUtils.globalLogger.info(
                'The file $name with ID $id and message  ${envelope.payload['message']} was sent.');
            break;
          default:
            GlobalUtils.globalLogger
                .info('A message was sent: ${envelope.content}');
        }
      });

I would have thought that once we killed the Internet connection, we would get some sort of message from the SDK indicating that connectivity to PubNub had been interrupted (we do in the Java SDK), but we don't. Is there a different Stream/listener we should be using?

are commented 2 years ago

Hi! Sorry for late reply and thanks for your patience!

By default, Dart SDK does not attempt reconnection. However, you can enable it with a RetryPolicy. When you do that, you can make use of internal events.

There is an undocumented property on the PubNub instance called pubnub.signals that contains a rudimentary way to notify the SDK about the network status and receive its internal events.

An example of that:

pubnub.signals.networkIsConnected.listen((isConnected) {
  if (isConnected) {
  } else {
  }
});

However, you have to keep in mind that this is not 100% reliable, because of the stateless nature of the underlying protocol. The connection can be dropped and the system may not notify the SDK at all - then the only information about it is going to be a timeout after 5 minutes. Please be careful with relying on this API.

tfrysinger commented 2 years ago

are -

Thanks for the reply.

Just so I am clear, the networkIsConnected property is updated when the connection to PubNub gets interrupted, not the general internet connection, is that correct?

In other words, setting this listener will get triggered (most of the time, I understand your comment about reliability) when a connection to PubNub itself gets interrupted even if the internet connection is still valid, correct?

Thanks!

tfrysinger commented 2 years ago

are -

Also you mention a timeout event - is that a separate aspect of pubnub.signals?

are commented 2 years ago

In other words, setting this listener will get triggered (most of the time, I understand your comment about reliability) when a connection to PubNub itself gets interrupted even if the internet connection is still valid, correct?

Yes, that is correct. It basically collects network errors in other requests to PubNub.

Also you mention a timeout event - is that a separate aspect of pubnub.signals?

Timeouts are defined for each request in the PubNub SDK - in case of subscribe, if 5 mintues pass without response from the server, the timeout will kick in and start a reconnection process to determine the cause. Signals and the timeout/reconnection are both part of the supervisor system.

tfrysinger commented 2 years ago

How does a timeout event manifest itself - will it come through the PubNub.subscribe subscription as a envelope.messageType of MessageType.signal? If so, what value in the envelope.content do I check to know it is a timeout event?

are commented 2 years ago

If timeout happens, it will be thrown as an exception on the message stream. However, with a RetryPolicy enabled it will be caught by the supervisor and a reconnection process will begin. This will trigger the pubnub.signals.networkIsConnected event.

tfrysinger commented 2 years ago

OK thanks - one last question. I am seeing cases where once I establish a subscription and I send a test message (or a signal) I don't see a response. Is there some sort of delay that isn't documented between when a subscribe() is performed and when it can be used to publish a message?

Here is my code (same as before, just added the publish at the end):

      statusSubscription?.messages.listen((envelope) {
        GlobalUtils.globalLogger.info("Envelope received on channel $statusChannel");
        switch (envelope.messageType) {
          case MessageType.normal:
            final String content = envelope.content;
            GlobalUtils.globalLogger
                .info('A normal message was received: $content');
            if (content == "CONNECTED") {
              for (InTouchSignallingServerListener listener in _listeners) {
                listener.onServerConnected();
              }
            }
            break;
          case MessageType.signal:
            GlobalUtils.globalLogger
                .info('A signal message was received: $envelope.content');
            break;
          case MessageType.objects:
            GlobalUtils.globalLogger.info(
                'An object event was received with data ${envelope.payload['data']}');
            break;
          case MessageType.messageAction:
            GlobalUtils.globalLogger.info(
                'A action message ${envelope.payload['event']} was received with data ${envelope.payload['data']}');
            break;
          case MessageType.file:
            var fileInfo = envelope.payload['file'];
            var id = fileInfo['id']; // unique file id
            var name = fileInfo['name']; // file name
            GlobalUtils.globalLogger.info(
                'The file $name with ID $id and message  ${envelope.payload['message']} was sent.');
            break;
          default:
            GlobalUtils.globalLogger
                .info('A message was sent: ${envelope.content}');
        }
      });
      pubNub
          ?.publish(statusChannel ?? 'statusChannel', "CONNECTED")
          .then((value) {
        GlobalUtils.globalLogger.info(
            "Connection signal sent successfully on channel ${(statusChannel ?? 'statusChannel')}");
      }).onError((error, stackTrace) {
        GlobalUtils.globalLogger.info(
            'An error occurred signaling our connection using channel ${(statusChannel ?? 'statusChannel')}: ${error.toString()}');
        for (InTouchSignallingServerListener listener in _listeners) {
          listener.onServerDidNotConnect();
        }
      });

I can see that the subscribe is completed and the connection message sent. However I only intermittently receive the "CONNECTED" message in the listener, which makes me wonder if there is some timing issue going on between when the subscribe happens and when PubNub can actually receive a publish request?