Xamarin — PCL vs. Shared Project
In Xamarin apps, the supported platforms share business logic that may represent more than a half of the project so, it’s in our best interest to organize this logic as well best.
The .NET Framework has two types of project, capable of providing code sharing, Portable Class Library (PCL) and Shared Project. This leads an obvious question: which one is the best?
Navigating through the online Xamarin community forums and blog posts, I noticed that most Xamarin developers defend that PCL projects are better suited for Xamarin development as the produced code is easier to read and maintain and is less error prone. Still, it is not consensual. Miguel de Icaza, the co-founder of Xamarin, advises users to use the Shared Project approach.
A Shared Project is not directly compiled. In other words, no DLL file is produced in the compilation process. Instead, the files are compiled into the same DLL as the project that references it. This way, it is possible to write blocks of platform specific code in the Shared Project that will only be compiled by the specific platform.
It is possible to write platform specific code for all the supported platforms in the same file by using compiler directives #ifdef. This way, the platform dependent code is organized in the same file for a given feature. For the debugging process, you don’t have to jump from file to file in order to follow code, as you do in a PCL.
There are downsides to this kind of project. Blocks of specific code make the implementation more complex and get more and more difficult to read as the implementation advances. Another problem is that, when writing code, only one compiler is active (the one referent to the selected project), whether it is Android, iOS or Windows. This can be a great hindrance when the number of lines of code increase and you have to do operations like refactoring code.
PCL projects are compiled in portable assemblies that run in any .NET based runtime. This means PCL projects can be included in the supported platforms’ executable files because all of the assemblies will run in the same runtime.
In the past, PCL projects did not have full access to the .Net Framework. You needed to select the platforms you wanted to support. These platforms didn’t have the same access to the .Net Framework so, the PCL was restricted to the lowest common denominator of the API of the selected platforms.
This disadvantage has been overcome with the .Net Standard adoption.
If you created your PCL before the .Net Standard adoption, you can change the target of your project to .Net Standard.
Unlike Shared Project, platform specific code cannot be implemented inside the PCL project. It must be implemented inside the platform specific projects. Now, this code must called from within the PLC. How do you call this code?
You may use Dependency Injection in order to call the platform specific operations from within the PCL project. Define an interface inside the shared code (PCL) and implement this interface on each of the supported platforms’ project to inject the class in the shared code. This way, it is possible to see the platform specific code as plugins that will integrate the shared code. There is a clear isolation of what is shared and what is platform dependent. This isolation also promotes unit testing.
On a side note, many developers consider Dependency Injection to be an anti-pattern. Removing the tight coupling between objects can have its benefits but also, its disadvantages.
So, which one is the best approach?
Both approaches seem valid to me and I can’t really tell which one is the best but I’ve been involved in several Xamarin projects and I must say that I never felt the need to use compiler directives. In my opinion, shared code restricted into compiling directives is more difficult to read, slowing down the development process.
Dependency Injection may be great solution to keep separation between shared code and platform specific code but, in smaller projects, this technique can add a lot of boilerplate code. In this scenario, Shared Projects may be a better solution as they are simpler in terms of architecture.
Shared Project does not mean that all of the shared code must be surrounded by compiler directives, it means you can use compiler directives. You can develop your code as you were using PCL, with Dependency Injection, and still have the possibility to use #ifdef in very particular cases, not producing spaghetti code.
Even if you don’t intend to use compiler directives, in my humble opinion, having the ability to, is potentially problematic to the project when the development isn’t fully under your control. I’m certain that you’ve worked with many developers who could be better at team working and don’t have the notion that their code will be read or modified in the future by other developers. If you’re one of them, you smell like all of these together, if not, maybe it is better if you don’t use Shared Project for big projects. Other team members may not be as careful as you using it.
Personally, if I was to start a Xamarin project, I would go for PCL, whether it was a small personal or big team project, forcing myself and others to keep code clean and easier to maintain.
If you still have any questions on this matter, disagree with something or if I’m missing something, please, feel free to comment. Thanks and see you soon!