Xamarin is powerful and it is getting popular day by day. The purpose of this post is educational and it intends to provide newbies a quick overview on building a Portable Class Library Project in C# that exposes a single method to request a RESTFULL client, retrieve JSON output, desacralize resultant string in objects and expose custom events that can be hooked by calling thread signaling either completion of routine or raising an error.

In a subsequent post I’ll make Xamarin based Android and native Windows Phone/Windows Store apps that will consume the Weather client exposed by this portable class library.

Please note that Portable HttpClient library is available for quite some time and exposes the Task based asynchronous methods that makes developers life a lot easier and I strongly suggest you to make use of HttpClient than raising custom events upon completion of task.

Begin with File > New Project > Portable Class Library  

Note that the library will work well with Xamarin based iOS and Android apps as well.

Delete the existing class, Class1.cs and let us organize the project namespaces by introducing folders as follows,

Client will contain the class that will request REST endpoint.

Events will server Event Arguments classes for our custom defined event

Response to host objects relative to JSON serialization

Now before we begin introducing classes, we know that upon requesting the weather RESTFULL API endpoint (http://api.openweathermap.org/data/2.5/weather?q=Lahore,pk) we’ll receive JSON output. We’ll have to deserialize JSON output in corresponding classes found in Response folder. Luckily I’ve covered the process of yielding JSON classes from JSON string in an earlier article however in this post we’ll see how to include NewtonSoft’s JSON DLL using Library Package Manager Console.

Go to Library Package Manager Console from Menu,

 

Once the Package Manager Console is activated, execute following command,

PM> install-package Newtonsoft.Json

This will download the relative library, include reference to portable DLL in our project (since we’re working on Portable Class Library).

Let’s go ahead and introduce EventArgs and WeatherHttpClient classes in our solution. Here’s how the code of WeatherHttpClient looks like,

 

 public delegate void RequestCompletedEventHandler(object sender, RequestEventArgs e);
public delegate void RequestErrorEventHandler(object sender, RequestEventArgs e);

public class WeatherHttpClient
{
public event RequestCompletedEventHandler RequestCompleted;
public event RequestErrorEventHandler RequestError;

public void Request(string location)
{
try
{
var request = HttpWebRequest.Create(@"http://api.openweathermap.org/data/2.5/weather?q=" + location);
request.Method = "GET";
var result = (IAsyncResult)request.BeginGetResponse(ResponseCallback, request);
}
catch (Exception ex)
{
if (RequestError != null)
{
//Raise Request Error (with custom Exception during response call back)
RequestError(this, new RequestEventArgs()
{
IsError = true,
ErrorMessage = Constants.REQUEST_EXCEPTION,
Exception = ex
});
}
}
}

private void ResponseCallback(IAsyncResult result)
{
try
{
var request = (HttpWebRequest)result.AsyncState;
var response = request.EndGetResponse(result);

using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
string contents = reader.ReadToEnd();
ParseJson(contents);
}
}
catch (Exception ex)
{
if (RequestError != null)
{
//Raise Sync Error (Custom Exception during response call back)
RequestError(this, new RequestEventArgs()
{
IsError = true,
ErrorMessage = Constants.REQUEST_CALLBACK_EXCEPTION,
Exception = ex
});
}
}
}

private void ParseJson(string jsonString)
{
try
{
RootObject rootObject = new RootObject();
rootObject = JsonConvert.DeserializeObject<RootObject>(jsonString);

if(RequestCompleted!= null)
{
RequestCompleted(this, new RequestEventArgs()
{
IsError = false,
ErrorMessage = string.Empty,
Exception = new NotSupportedException(Constants.GRACEFUL_EXECUTION_MESSAGE),
RootObject = rootObject
});
}
}
catch (Exception ex)
{
if(RequestError!= null)
{
//Raise Sync Error (Custom Exception during parsing)
RequestError(this, new RequestEventArgs()
{
IsError = true,
ErrorMessage = Constants.REQUEST_PARSE_JSON_EXCEPTION,
Exception = ex
});
}
}
}
}

Please note that in case of an exception or graceful execution we raise different event however a shared RequestEventArgs class is used,

 public class RequestEventArgs : EventArgs
{
public RequestEventArgs()
{
ErrorMessage = string.Empty;
IsError = false;
RootObject = new RootObject();
Exception = new NotImplementedException(Constants.NOT_IMPLEMENTED_EXCEPTION_MESSAGE);
}

/// <summary>
/// Will denote whether the call was successful or not
/// </summary>
public bool IsError
{
get;
set;
}

/// <summary>
/// In case of an error, the error message
/// </summary>
public string ErrorMessage
{
get;
set;
}

/// <summary>
/// Refers to Inner exception within Weather Client (if any)
/// </summary>
public Exception Exception
{
get;
set;
}

/// <summary>
/// Root Object will host the JSON deserialized POCO classes in case of no error
/// </summary>
public RootObject RootObject
{
get;
set;
}

}

 

Where Constants can be found in Constants.cs file kept on root of WeatherClient project,

public class Constants
{
public const string REQUEST_EXCEPTION = "Error while making http request.";
public const string REQUEST_PARSE_JSON_EXCEPTION = "Error in parsing JSON output.";
public const string REQUEST_CALLBACK_EXCEPTION = "Please ensure your device is connected with the internet.";

public const string NOT_IMPLEMENTED_EXCEPTION_MESSAGE = "Request is unitialized";
public const string GRACEFUL_EXECUTION_MESSAGE = "The last request completed gracefully without any exception.";
}

To keep the source code size small, other DLLs from Netwonsoft’s packages are deleted than referenced one (\Newtonsoft.Json.6.0.1\lib\portable-net40+sl5+wp80+win8+monotouch+monoandroid\ at the time of this writing).