We’re on to part 2 of the ongoing series of sharing code across platforms using Xamarin. The first partcoveredthe why and when to share code,part number 2 was about Shared Projects, and today we’ll cover the finalway to share code across platforms.
If you remember, there are 3 ways to share code across platforms:
- Linked Files
- Shared Projects
- Portable Class Libraries
We’re not going to bother with Linked Files, since those are falling out of favor. Part number 2 covered Shared Projects. So last, but certainly not least, let’s talk about Portable Class Libraries, or PCLs.
What are Portable Class Libraries?
First off, Portable Class Library is way too long to type, so we’re going to call them PCLs from here on out. Now that we have that established, let’s answer the question of what exactly is a PCL?
This means that unlike a Shared Library, a PCL actually creates a DLL, and thatDLL can then be added to any project in the same way that it would normallyreference aDLL. The key difference however, is that the referencing projectwhich the PCL DLL is being added to needs to support the same or a more verbose profile than the PCL (aprofile is simply a designation of which features of the BCL the PCL has access to).
Simply: A PCL can be used across platforms without recompiling!!
Another way in which PCLs differ from Shared Projects is that other DLLs can be referenced from within the PCL project. (Again of course, assuming the DLL is compatible with the PCL’s profile). That means that 3rd party libraries like JSON.NET, MVVM Light or NInject have a PCL port which can be referenced… we can even use NuGet or the Xamarin Component Store!
Pros and Cons of PCLs
Advantages
We covered some of the advantages above – the main point isthat PCLs emit a DLL which than can be referenced from other projects. References to other DLLs can be addedto the PCL as well.
Because PCLs consolidate common codeinto a single library referenced from many different projects, unit testing the shared code is straight forward. All of the code is in one place, only have to write unit tests once against that code.
Refactoring PCLs is much easier than with Shared Projects. No preprocessor directive are used to delineate platform specific code, thus when refactoring we don’t have to worry about ‘disabled’ codebased on which platform is currently active.
Disadvantages
The main disadvantage issome libraries we have come to rely on may not be available within the PCL. Because we’re only targeting some features of the BCL, the whole .NET Framework is not available.
Also, no platform specific code or libraries which are platform specific, can be included in a PCL. There are some ways one can overcome this by using dependency injection or partial classes, but we’ll cover those in a future post.
Finally, the design ofthe PCL can bemore complexthan a Shared Library. Because the whole .NET platform is not available, considerations must be made in how a particular task gets accomplished. (We’ll see more on that in the example below). It’s debatable whether more design work should be classified as a “disadvantage”, but it’s good to know going in.
Example
Let’s get right down to it and implement an example of using a PCL library across platforms. We’ll be creating a new version of the Tabata app used in the Shared Project portion of this series. In the Shared Project version of that app, we relied on preprocessor directives to explicitly tell the library which platform was currently being used in order to read and save files on the OS. Obviously, this created a dependency from the Shared Project to the platform. Here, we’ll eliminatethat dependency in the PCL andhave the referencing platform projects handle the filesystem access in their own way.
Again, the first step in creating any cross platform library is to identify the code which can be shared. Just as before, we’ll model the individual Tabata with properties of Date, Work Interval, Rest Interval and Number of Sets. We’ll also have a list class which contains a history of all of the completed exercises and the ability to read them from the saved CSVfile.
Next, we’ll create a solution which just has the PCL project in it (we’ll add the resulting compiled DLL to the specific platform projects later). First create a new Portable Library from the Add->New Project menu. (We’re using Xamarin Studio, but one could use Visual Studio in much the same way). The Portable Library option is found under the C# node.
From there you’ll notice that under the References folder, instead of seeing references to System, System.Core, etc. we have a reference to .NET Portable Subset. Opening the node will enumerate exactly which libraries are available in the profile.
To change the profile being targeted, go to PCL’s project options dialog and select Build->General. This will display both the current profile as well as which platforms can use the PCL. Changing the Current Profile will automatically select different platforms the profile supports. On the flip side, changing anything under the Target Frameworks will automatically select a different profile that supports the frameworks. We’ll go with profile 78 as it seems the most stable with Xamarin for the time being.
Next we’ll implement the core logic of the Tabata itself. The class is the same as the previous one, except it does not access the file system in a platform specific way, rather a StreamReader or StreamWriter object is passed in, which the calling platform has already initialized.
public async void SaveTabata(StreamWriter file)
{
this.TabataDate = DateTime.Now;
string tabataInfo = string.Format (“{0},{1},{2},{3}“, this.WorkInterval, this.RestInterval,this.NumberOfSets, this.TabataDate.ToString ());
await file.WriteLineAsync (tabataInfo);
}
We also had to come up with a new way to “countdown” the remaining seconds in the Tabata. The System.Timers namespace used in the Shared Project example is not present in the PCL profile being used. Instead we’re running a task and awaiting a delay of 1 second each time.
var t = Task.Run (async delegate {
while (keepOnLooping) {
await Task.Delay (1000);
// See GitHub for the rest of the code!
The specific implementation is on GitHub here:https://github.com/codemillmatt/TabataPCL
The repository linked to above is only that of the PCL library itself. The consumption of that PCL happens in a repository with an Android and iOS project. I wanted to put these in a distinct repository to demonstrate different types of projects consuming the DLL generated from the PCL project. The GitHub repository is here:https://github.com/codemillmatt/TabataPCLConsumer
Summary
We covered what exactly a PCL is, and also touched on some of the pros and cons of using one. The big thing to remember here is that PCLs can be used across platforms and even across different solutions – but that portability comes at a cost, namely that the full .NET Framework is not available within the PCL.
In the next post in this series, we’ll talk about how to overcome some of the limitations of PCLs not having full access to .NET or platform specific code by using dependency injection and partial classes.
Comments