Using Async Await in your Xamarin app with a Native Library

In late 2015, we started to work on adding the new Braintree vZero native SDK to one of our projects. Braintree is a payment solution provider owned by Paypal. Unfortunately, there was no binding library for Android and the existing solution for iOS had it’s own issues (mainly the fact that it was not supporting iOS 7), and so we had to do both bindings ourselves. Since we have an existing flow in the app and we use the MVVM pattern, we isolated the new Braitree vZero interaction in it’s own service that has specific implementations for iOS and Android. Finally to ensure that we were surfacing a nicer API (more C#-friendly) and to reduce the amount of boilerplate code, we wrote a new set of calls to ensure that we hid the callback calls and the listeners mechanism.

This had the effect of making the native API appears to behave more like a .NET implementation then the Callback based iOS or the listener driven Android implementations. The objective of this was also to make sure that if we use this binding library in an other project, we only had to write a minimum amount of code and not to write the same boilerplate code again. In .NET the current way to implement asynchronous operations is to use the async-await framework based on the Task object.

Android

I will start with our approach on Android. The Braintree vZero SDK uses Listeners to notify the application of the result of the authorization. We then use the TaskCompletionSource to generate a Task object that will complete when TrySetException, TrySetCanceled or TrySetResultis executed.

As you can see, we manage one of 3 results. If an error occurs, we convert the Java throwable object to a actual .NET Exception and then we set it in the TaskCompletionSource object. If the user cancels, we trigger a TaskCancelException with TrySetCanceled, and if we get the payment nonce, we return that object as the actual result we are waiting on.

We then use this new listener to get a nonce (a string returned by the SDK to represent a payment method)  from Braintree. For instance to get a Paypal nonce we do:

We generate an instance of the PaymentMethodNonceListener and add it to the Listeners of the Braintree Fragment partial class (the UI of the Braintree SDK requesting a payment from the User). Then we setup the PayPal authorisation flow by calling AuthorizeAccount andRequestBillingAgreement (if needed). Afterwards we await the listener. This order is actually important, we cannot wait on the listener until we add the listeners to the fragment, nor can we wait on the task before we do the authorization requests because doing so would break the flow. We do generate the listener first since we need to make sure we are ready to receive the information (error, success or cancel states) and because it’s asynchronous, it might be possible to have this before we are actually waiting on the task. To ensure that we don’t leave anything in the Fragment, and therefore to ensure we don’t have memory leaks, we remove the listener at the end in a finally block. We don’t catch the exceptions since the app needs to be able to handle errors if they come.

Finally on the app’s side we get the Braintree fragment and add it to the current activity (done in the GenerateBraintreeFragment method) then we setup the required parameters to this new async enabled call and await it.

In conclusion, we don’t have to create a Listener object on the app side since that boilerplate code is handled in the library itself. We can then reuse the binding library in future apps without having to worry about the listener.

iOS

For iOS, we followed a similar approach. We created a new method that was returning a Task instead of using the default method with a callback. So here’s an example using the same PayPal payment method:

For the iOS side of things, we do essentially the same thing. We wait on the response from Braintree using the Task-TaskCompletionSource couple and setting the eventual exception or result when needed.

Similar to the Android version, we prepare the Braintree SDK UI (here named APIClient instead of BraintreeFragment). We then need to prepare the PaypalDriver (the Paypal form of payment on iOS) and then we simply await the async enabled call instead of having to use a callback on the application side. This makes a cleaner and more streamlined interface. There is some difference between iOS and Android, like the use of PaypalDriver and the Card classes instead of using an unified call like the Android braintree fragment but both uses the async framework to manage the various calls.

Conclusion

Obviously, we only wrote the bindings we needed for our project. We did however include theDropIn library in the native binding libraries because we initially tough we would be using it. The same can be said of Android and Apple payment support. Both are included in their respective native binding libraries but the async framework additions are not done on them. Since we are planning to eventually add support for the platform specific payment system, this will be revisited and included.

The source code of the binding libraries is available on GitHub.

Recommended Posts

Laissez un commentaire