RingCentral C# SDK 1.0.0 has been released
This is the first major release of the C# SDK which is now available on Nuget. It contains a long list of bug fixes, improvements as well as some new features. A discussion of our Portable Class Library (PCL) support and improvements follows, while a list of bug fixes is available by viewing the GitHub closed issues list.
Improved Portable Class Library
Portable Class Libraries can help you to reduce the time and costs of developing and testing code. Use this project type to write and build portable .NET Framework assemblies, and then reference those assemblies from apps that target multiple platforms such as Windows, OS X, Xamarin.iOS and Xamarin.Android.
In earlier versions of the SDK, we had different SDK projects for different platforms. On some platforms, users even have to compile the code themselves. We turned to PCL (Portable Class Library) with this release. No matter what platform you are targeting, you can install our SDK via NuGet: Install-Package RingCentralSDK, no more manual compiling and dependency management.
The biggest challenge for us when switching to PCL is to find libraries which are PCL compatible. Our SDK is built upon some open source libraries which also have to be PCL compatible. First of all, we’d like to thank PubNub for publishing their SDK as a PCL, PubnubPCL which is a core dependency of our SDK. Additionally, we ran into issues with crypto. We were using PCLCrypto for message decryption, however, we found that the code didn’t work on iOS. We spent days of time to debug and finally we came to the conclusion that PCLCrypto didn’t work on iOS. It should work but for some weird reason we were not able to make it work. We did a lot more experiments and testing before switching to BouncyCastle for decryption which worked like a charm on all our target platforms including iOS.
Another challenge is the inconsistency between .NET on Windows and Mono on other platforms. For example, the SDK provides the ability to send faxes via a multipart MIME request, using either multipart/mixed or the recently added multipart/form-data. Using multipart/mixed message, we need a boundary string. In lots of places we were using a hard-coded string Boundary_1_14413901_1361871080888 as the boundary string and it works pretty well. But it turned out that the Mono implementation of .NET specification always resulted in exceptions. The root cause of this issue was very hard to identify because Mono was supposed to behave exactly the same as Microsoft .NET.
New Features & Improvements
OAuth 2 Authorization Code Flow
Authorization code flow which is also known as “a 3-legged authorization flow” protects user credentials and lets users control what they share with your app. You are required to use this flow if your app is a web app and is a public app that will be used by more than one organization.
The C# SDK provides two helper methods to make the implementation of Authorization Code Flow a piece of cake. The first helper method is for generating the authorization URI:
var authorizeUri = sdk.Platform.AuthorizeUri(redirectUri, myState);
Open authorizeUri in browser and let user login and authorize your app. Then user will be redirected to redirectUri where you can get the authCode from its url.
With authCode obtained, simply call the second helper method to do the authentication:
sdk.Platform.Authenticate(authCode, redirectUri)
Live Testing
Previously, we only did mock tests which we found were not sufficient to mock all the real world situations. It is necessary to perform tests against a live API server in order to make sure the server and the SDK is actually behaving as expected.
In C# SDK 1.0.0, we have RingCentral.Test.Real in addition to RingCentral.Test.Mock. The former is live testing while the latter is mock testing.
API simplification
Before 1.0.0, you need the following code in order to create a new subscription:
var subscription = new SubscriptionServiceImplementation(){ _platform = ringCentral };
1.0.0 has simplified the code to:
var subscription = sdk.CreateSubscription();
Before 1.0.0, if you want to do HTTP method tunneling, you need to change the HTTP header directly:
request.SetXhttpOverRideHeader("POST");
1.0.0 provides more convenient way:
request.HttpMethodTunneling = true;
Do things the C# way
Properties vs. Methods
In C#, methods represent actions and properties represent data. Properties are meant to be used like fields, meaning that properties should not be computationally complex or produce side effects. When it does not violate these guidelines, consider using a property, rather than a method.
We’ve changed lots of methods to properties to simplify the code. examples: sdk.Platform instead of sdk.GetPlatform(),response.Status instead of response.GetStatus().
Events vs. Callbacks
Unlike other programming languages, C# has a special keyword named event. An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. Events provide a generally useful way for objects to signal state changes that may be useful to clients of that object.
Unlike callbacks, events are good for recurring notifications that can be received by an arbitrary number of listeners. We take advantages of C# events when we are writing code for PubNub subscription. Here is some sample code:
subscription.NotificationEvent += (sender, args) => {
Console.WriteLine(args.Message);
};
Coding Conventions
Constant Casing
In C#, Microsoft StyleCop uses Pascal casing for constants. So the following doesn’t comply to C# coding style:
public const string SANDBOX_SERVER = "https://platform.devtest.ringcentral.com";
While the following passes:
public const string SandboxServer = "https://platform.devtest.ringcentral.com";
Curly Braces
It is considered a bad practice to omit curly braces:
if (_enableSSL) _pubnub = new Pubnub(publishKey, subscribeKey, "", "", _enableSSL);
else _pubnub = new Pubnub(publishKey, subscribeKey);
We’ve made it easier to read by changing it to the following:
if (_enableSSL)
{
_pubnub = new Pubnub(publishKey, subscribeKey, "", "", _enableSSL);
}
else
{
_pubnub = new Pubnub(publishKey, subscribeKey);
}