Categories
Bluetooth Xamarin

Asyncify a method and event response

It’s common to come across a pattern where you call a synchronous method to kick off an activity then await an event which gives you the result (or an error). In the process of building 32feet‘s Bluetooth LE library this became a familiar sight both for Android and iOS APIs.

The goal was to create a single async method which starts the process and completes when the response (good or bad) is received. The API model for the resulting cross-platform API is based on Web Bluetooth but with some extensions. The Web Bluetooth API is JavaScript based and each operation returns a promise – the .NET approach is to use a Task which you can await or ContinueWith.

In an early implementation I had used a WaitHandle and had a thread blocking on this but this is not a clean async approach. I needed a solution which would handle the method and event handler and use a standard Task. Luckily there is the TaskCompletionSource type which can facilitate this. It wraps a Task and allows you to set a result (or exception) which we’ll be setting inside the event handler. Therefore each resulting method follows basically the pattern below:-

TaskCompletionSource<IReadOnlyList<GattDescriptor>> tcs = new TaskCompletionSource<IReadOnlyList<GattDescriptor>>();
CBPeripheral peripheral = Service.Device;

void handler(object sender, CBCharacteristicEventArgs args)
{
   peripheral.DiscoveredDescriptor -= handler;

   if (args.Error != null)
   {
      tcs.SetException(new Exception(args.Error.ToString()));
      return;
   }

   List<GattDescriptor> descriptors = new List<GattDescriptor>();

   foreach (CBDescriptor cbdescriptor in _characteristic.Descriptors)
   {
      descriptors.Add(new GattDescriptor(this, cbdescriptor));
   }

   tcs.SetResult(descriptors.AsReadOnly());
}

peripheral.DiscoveredDescriptor += handler;
peripheral.DiscoverDescriptors(_characteristic);

return tcs.Task;
  • Create a TaskCompletionSource with the required return type
  • Get the native object we’ll be starting the operation on
  • Create a handler for the event. Inside the handler we
    • Remove the event handler
    • Check the response – set either positive or negative result to TaskCompletionSource.
  • Add the event handler
  • Call the method to start the process
  • Return the TaskCompletionSource Task

I hope this provides a handy pattern to follow if you’re trying to use similar APIs, if not I’m sure it will at least jog my memory. I’ll post again soon about the full Bluetooth library in more depth.

By Peter Freeman Foot

Microsoft Windows Development MVP