2

I'm trying to use ReactiveUI ReactiveCommands to switch on and off a gRPC stream that I've converted into an observable.

The code shown below works to some extent - the connect button will cause the stream to connect, and I start receiving data in the onNext handler of the subscribe. The disconnect button does also disconnect the stream via the cancellation token.

However, once the disconnect command is executed, I would also like to be notified so I can clear up some other state in the application. I understand that the onCompleted of the ReactiveCommand never gets called, because at any point it could be executed again, so my question is - how can I know when the stream has been switched off?

View

this.WhenActivated(d =>
{
    d(this.BindCommand(ViewModel, x => x.ConnectCommand, x => x.Connect));
    d(this.BindCommand(ViewModel, x => x.DisconnectCommand, x => x.Disconnect));
});

ViewModel

ConnectCommand = ReactiveCommand.CreateFromObservable(
    () => appService.ApplicationStream(request)
        .TakeUntil(DisconnectCommand));
ConnectCommand.Subscribe(
    resp => 
    { 
        _logger.Debug(resp); 
    },
    () => 
    {
        // Ideally I could do something useful here, but https://stackoverflow.com/a/26599880/57215
        _logger.Debug("Never called, ReactiveCommands never OnComplete"); 
    });

ConnectCommand.IsExecuting.Subscribe(x => _logger.Debug($"is executing: {x}"));
ConnectCommand.CanExecute.Subscribe(x => _logger.Debug($"can execute: {x}"));
ConnectCommand.ThrownExceptions.Subscribe(ex =>
    throw new Exception($"Could not get data from server: {ex}"));

DisconnectCommand = ReactiveCommand.Create(
    () => { },
    ConnectCommand.IsExecuting);

Service

public IObservable<ApplicationStreamResponse> ApplicationStream(ApplicationStreamRequest request)
{
    return Observable.Create<ApplicationStreamResponse>(async (observer, token) =>
    {
        try
        {
            using (var call = _client.ApplicationStream(request, cancellationToken: token))
            {

                while (await call.ResponseStream.MoveNext())
                {
                    if (token.IsCancellationRequested) return;
                    observer.OnNext(call.ResponseStream.Current);
                }
                observer.OnCompleted();
            }
        }
        catch (RpcException e)
        {
            if (e.Status.StatusCode == StatusCode.Cancelled)
            {
                _logger.Debug($"Application stream was disconnected: {e.Status.Detail}");
                observer.OnCompleted();
            }
            observer.OnError(e);
        }
    });
}
MarkNS
  • 3,811
  • 2
  • 43
  • 60
  • In a fairly typical example of SO rubber duck debugging, I've come to the conclusion that I shouldn't try and figure out when the observable has completed in the service, but rather just use the action in the disconnect command to clear the application state when disconnected. I wanted to post the question anyway to ask if people have comments about the code in general - I'm very new to ReactiveUI and just hoping that things are roughly correct! :) – MarkNS Oct 19 '17 at 14:34

1 Answers1

0

Subscribe to the command:

d(this.BindCommand(ViewModel, x => x.DisconnectCommand, x => x.Disconnect));
this.ViewModel.DisconnectCommand
    .Subscribe(_ => { /* command finished */});

Or create a bool reative property, set it to true at the end of the DisconnectCommand code, and check the value in the view.

Luis
  • 436
  • 4
  • 11