RunSafe Tasks / Commands wrapper in Xamarin

Table of Contents

When we work with Task most of the time we do common implementations of scenarios where we use repetitive code. For example use IsBusy, try-catch, Loggin, etc.

Well, the idea is to make a wrapper that allows us to store all this logic for us to reuse. So, let’s get to it.

Task Wrapper

Let’s start at the beginning. What is a Task Wrapper? In general, a Task Wrapper is a task, method, or process that “wraps” or “encapsulates” some functionality.

These are useful when providing a level of deployment abstraction; for example, container tasks that involve administrative invocation process logic without disturbing code that is called with it.

They can also simplify the use of the underlying object by reducing the number of interface points involved; This often allows for safer use of the underlying components. Not to mention it makes it much easier to support or adapt for all your implementations.

In order to understand the whole context, let’s look at a common scenario:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
private async Task DoSomething()
{
try
{
if (IsBusy) return;
IsBusy = true;
// Your code goes here
}
catch (Exception e)
{
// Your code goes here
}
finally
{
IsBusy = false;
}
}
...
... private async Task DoSomething() { try { if (IsBusy) return; IsBusy = true; // Your code goes here } catch (Exception e) { // Your code goes here } finally { IsBusy = false; } } ...
...
        private async Task DoSomething()
        {
            try
            {
                if (IsBusy) return;

                IsBusy = true;

                // Your code goes here
            }
            catch (Exception e)
            {
                // Your code goes here
            }
            finally
            {
                IsBusy = false;
            }
        }
...

The example shown above is a common scenario where we are most likely repeating the same code (try-catch and IsBussy) in all the actions you execute.

To avoid this, we will use a RunSafe Task Wrapper. Let’s see below.

RunSafe Task Wrapper

To evade repetitive code what we’re going to do is create a method that we can use globally in all of our ViewModels. That is, we must create a method similar to the following in our BaseViewModel. Let’s see:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
protected virtual async Task RunSafe(Task task, Action<Exception> onError = null)
{
try
{
if (IsBusy) return;
IsBusy = true;
await task;
}
catch (Exception e)
{
onError?.Invoke(e);
}
finally
{
IsBusy = false;
}
}
...
... protected virtual async Task RunSafe(Task task, Action<Exception> onError = null) { try { if (IsBusy) return; IsBusy = true; await task; } catch (Exception e) { onError?.Invoke(e); } finally { IsBusy = false; } } ...
...
        protected virtual async Task RunSafe(Task task, Action<Exception> onError = null)
        {
            try
            {
                if (IsBusy) return;

                IsBusy = true;

                await task;
            }
            catch (Exception e)
            {
                onError?.Invoke(e);
            }
            finally
            {
                IsBusy = false;
            }
        }
...

Here are several things to keep in mind:

  • We can execute an action (Action<Exception>) that receives an exception for us to be able to handle the context in case something happens. For example: Update the status of your app.
  • We can add other properties like Loggin or Crashes.TrackError(exception, properties) from App Center to track our exceptions and more.
  • We can also store more global logic such as displaying states in the app or simply handling a TimeOut for our tasks.
  • Etc.

With this wrapper, we removed all that repetitive code, and we would use it as follows.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
// Normal Implementation
await RunSafe(MyMethod(parameter));
// Command Implementation
MyCommand = new Command(async () => await RunSafe(MyMethod(parameter)));
...
... // Normal Implementation await RunSafe(MyMethod(parameter)); // Command Implementation MyCommand = new Command(async () => await RunSafe(MyMethod(parameter))); ...
...
        // Normal Implementation
        await RunSafe(MyMethod(parameter));

       // Command Implementation
       MyCommand = new Command(async () => await RunSafe(MyMethod(parameter)));
...

RunSafe Command Wrapper

With the above, we should work without any problem, but those who are using ReactiveUI can do this with commands.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
protected virtual void RegisterSafeCommand<T, U>(ReactiveCommand<T, U> command, Action<Exception> onError = null)
{
command.IsExecuting.Subscribe(x =>
{
this.IsBusy = x;
}, _ => this.IsBusy = false);
command.ThrownExceptions.Subscribe(exception =>
{
onError?.Invoke(exception);
});
}
...
// OR
...
protected virtual void RegisterBusyCommand<T, U>(ReactiveCommand<T, U> command) =>
command.IsExecuting.Subscribe(
x => this.IsBusy = x,
_ => this.IsBusy = false,
() => this.IsBusy = false
);
...
//Then we can have an implementation similar to this:
...
MyCommand = ReactiveCommand.CreateFromTask(MyMethod);
RegisterSafeCommand(MyCommand , _ => Console.WriteLine("Something Happens"));
...
... protected virtual void RegisterSafeCommand<T, U>(ReactiveCommand<T, U> command, Action<Exception> onError = null) { command.IsExecuting.Subscribe(x => { this.IsBusy = x; }, _ => this.IsBusy = false); command.ThrownExceptions.Subscribe(exception => { onError?.Invoke(exception); }); } ... // OR ... protected virtual void RegisterBusyCommand<T, U>(ReactiveCommand<T, U> command) => command.IsExecuting.Subscribe( x => this.IsBusy = x, _ => this.IsBusy = false, () => this.IsBusy = false ); ... //Then we can have an implementation similar to this: ... MyCommand = ReactiveCommand.CreateFromTask(MyMethod); RegisterSafeCommand(MyCommand , _ => Console.WriteLine("Something Happens")); ...
...
        protected virtual void RegisterSafeCommand<T, U>(ReactiveCommand<T, U> command, Action<Exception> onError = null)
        {
            command.IsExecuting.Subscribe(x =>
            {
                this.IsBusy = x;
            }, _ => this.IsBusy = false); 

            command.ThrownExceptions.Subscribe(exception =>
            {
                onError?.Invoke(exception);
            });
        }
...

// OR

...
        protected virtual void RegisterBusyCommand<T, U>(ReactiveCommand<T, U> command) =>
           command.IsExecuting.Subscribe(
               x => this.IsBusy = x,
               _ => this.IsBusy = false,
               () => this.IsBusy = false
           );
...

//Then we can have an implementation similar to this:

...
        MyCommand = ReactiveCommand.CreateFromTask(MyMethod);
        RegisterSafeCommand(MyCommand , _ => Console.WriteLine("Something Happens"));
...

As you can see, these are some apps you can make with commands, but these may vary depending on your needs.

conclusion

As I promised, this article can help you handle issues in your business logic globally. And if you’re new, keep reading the previous article to handle global error in Xamarin.

I hope this article will be very useful to you, and for any comments leave it in the box below. If you want me to write about specific content let me know on my social media and if your request likes it very much it is very likely that we will see it here.

Well, remind you that I’m sharing a lot of content on Twitter and LinkedIn for those who want to take a turn over there.

With nothing else to add, see you next time!

Share this content!

Facebook
Twitter
LinkedIn
Telegram
WhatsApp