Xamarin — .NET Standard vs. Shared Project
Typically, the supported platforms of a Xamarin app share the business logic. This shared logic can represent more than a half of the project. It’s in our best interest to keep this shared code as well organised as possible.
The .NET Framework provide different ways to share code between Android and iOS apps. The most used are .NET Standard 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 .NET Standard projects are better suited for Xamarin development as the produced code is easier to read and maintain and less error prone. Though, it’s not consensual. Miguel de Icaza, co-founder of Xamarin, for instance, preferes 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 organised 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 .NET Standard.
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.
.NET Standard projects are compiled in portable assemblies that run in any .NET based runtime. This means .NET Standard projects can be included in the supported platforms’ executable files because all of the assemblies will run in the same runtime.
Portable Class Libraries (PCL) are another approach to build a shared library. It became less and less used with the adoption of .NET Standard.
PCL projects do not have full access to the .Net Framework. You need to select the platforms you want to support. These platforms don’t have the same access to the .Net Framework so, the PCL is restricted to the lowest common denominator of the APIs of the selected platforms. This disadvantage doesn’t exist in .Net Standard.
It’s possible to convert a PCL to a .NET Standard project by changing the target of your project to .Net Standard.
Unlike Shared Project, platform specific code cannot be implemented inside the .NET Standard project. It must be implemented inside the platform specific projects. Now, this code must called from within the .NET Standard project. How do you call this code?
You may use Dependency Injection in order to call the platform specific operations from within the .NET Standard project. Define an interface inside the shared code 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 .NET Standard, 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 .NET Standard, 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!