Using Xamarin Forms with .NET Standard - VS 2017 Edition
I have previously blogged about using .NET Standard with Xamarin Forms. Since then, the tooling has changed significantly with Visual Studio 2017 and Visual Studio for Mac. This post will show you what you need to use
Xamarin.Forms with a .NET Standard class library.
Why use a .NET Standard class library instead of a PCL? There are many good reasons, but the two biggest ones are:
- Much bigger surface area. PCL's were the least common denominator intersection of supported platforms. The end result is that while the binary worked on many platforms, there were a much more limited set of APIs available. .NET Standard 1.4 is the version that supports UWP, Xamarin Android, Xamarin iOS, and Xamarin.Mac.
- "SDK Style" project file goodness. Legacy PCL's use the old
csprojformat which have tons of gunk in them. While it is possible to use the new project style to generate legacy PCLs (if you use my MSBuild.Sdk.Extras package), it's time to move past those. If you target .NET Standard 1.0-1.2, some PCL profiles can install your library. See the full table for the list.
Using .NET Standard requires you to use
PackageReference to eliminate the pain of "lots of packages" as well as properly handle transitive dependencies. While you may be able to use .NET Standard without
PackageReference, I wouldn't recommend it.
You'll need to use one of the following tools:
As of now, the project templates for creating a new Xamarin Forms project start with an older-style
packages.config template, so whether you create a new project or have an existing project, the steps will be pretty much the same.
Step 1: Convert your projects to use
PackageReference. The NuGet blog has details on using
PackageReference with all project types. Unfortunately there's no current migration tool, so it's probably easiest to uninstall your existing packages, make sure the
packages.config file is gone and then install the package after setting the VS Options to
PackageReference. You can also do it by hand (which is what I did for my projects).
Step 2: As part of this, you can remove dependencies from your "head" projects that are referenced by your other projects you reference. This should simplify things dramatically for most projects. In the future, when you want to update to the next Xamarin Forms version, you can update it in one place, not 3-4 places. It also means, you only need the main
Xamarin.Forms package, not each of the packages it pulls in.
For now, you'll need to add the
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> property near the top of your iOS and Android
csproj files. That tells NuGet restore to use the
PackageReference mode even if you don't have any direct packages (this is important for transitive restore). If you have any
PackageReference elements in your iOS or Android
csproj, then you don't need this. For UWP, you already should have a
PackageReference to the UWP meta-package (
Microsoft.NETCore.UniversalWindowsPlatform version 5.3.2).
If you hit any issues with binaries not showing up in your
bin directories (for your Android and iOS "head" projects), make sure that you have set
true in your
At this point, your project should be compiling and working, but not yet using
Step 3: Move your PCL library to .NET Standard. This is the hard part today as there's no tooling to automatically do this correctly. Be warned and DO NOT use this option in the PCL properties. It is broken and will create a
project.json based library targeting
dotnet. I hope this option is removed in a future VS Update! Instead, go to
File -> New Project -> .NET Standard -> Class Library and create a new class library. If this is a new project, I'd simply delete the existing PCL and just use a new one. If it's an existing project, you'll want to migrate. The new format is far simpler and moving the PCL by hand is usually pretty easy. What I've usually done is this:
- Close the solution in VS
- Take the existing
csprojand make a copy of it somewhere else. I'll keep this other copy open in Notepad.
- Copy/paste the contents of the new project you created and replace the contents of your existing project. Most of what you had in the old project isn't really needed anymore. What you'll likely need are settings like any signing or assembly names that don't match the folder name/conventions. If you have ResX files with design-time generated code, you'll need to add the following. Likewise, for Xamarin Forms pages, you'll need this.
- Decide which .NET Standard version to target, probably 1.4, based on the table. Here's a cheat sheet:
- If you only want to support iOS and Android, you can use .NET Standard 1.6. In practicality though, most features are currently available at .NET Standard 1.3 and up.
- If you want to support iOS, Android and UWP, then NET Standard 1.4 is the highest you can use.
- If you want to support Windows Phone App 8.1 and Windows 8.1, then NET Standard 1.2 is your target.
- If you're still supporting Windows 8, .NET Standard 1.1 is for you.
- Finally, if you need to support Windows Phone 8 Silverlight, then .NET Standard 1.0 is your only option.
Once you determine the
netstandard version you want, in your csproj, set the
TargetFramework to it --
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard1.4</TargetFramework> <PackageTargetFallback>portable-net45+win8+wpa81+wp8</PackageTargetFallback> <DebugType>full</DebugType> </PropertyGroup> <ItemGroup> <PackageReference Include="Xamarin.Forms" Version="18.104.22.168" /> </ItemGroup> <ItemGroup> <!-- https://bugzilla.xamarin.com/show_bug.cgi?id=55591 --> <None Remove="**\*.xaml" /> <Compile Update="**\*.xaml.cs" DependentUpon="%(Filename)" /> <EmbeddedResource Include="**\*.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" /> </ItemGroup> </Project>
Note the addition of the
PackageTargetFallback property. This is required to tell NuGet that specified TFM is compatible here because the
Xamarin.Forms package has not yet been updated to use
netstandard directly. Also note that
DebugType set to
full is required for the Xamarin tool-chain currently as they don't yet support the new portable PDBs that are created by default.
At this point, when you reload the project, it should restore the packages and build correctly. You may need to do a full clean/rebuild.
Seeing it in action
I created a sample solution showing this all working over on GitHub. It's a good idea to clone, build and run it to ensure your environment and tooling is up-to-date. If you get stuck converting your own projects, I'd recommend referring back to that repo to find the difference.
Building on command line
You will need to use
MSBuild.exe to build this, either on Windows with a VS 2017 command prompt or a Mac with Visual Studio for Mac. You cannot use
dotnet build for these projects types.
dotnet build only supports .NET Standard, .NET Core and .NET Framework project types. It is not able to build the Xamarin projects and the custom tasks in Xamarin Forms have not yet been updated to support .NET Core.
To build, you'll need two steps:
msbuild /t:build /p:Configuration=ReleaseMySolution.sln
You can also restore/build the
.csproj files individually if you'd prefer.
As always, feel free to tweet me @onovotny as well.