I love multi-targeting for creating libraries, and it is at the core of all of my plugins and Xamarin.Essentials. If you don't know what multi-targeting is don't worry as it is pretty rare that you would need to use it if you are creating apps. Most likely you will want to use multi-targeting in creating cross-platform libraries. In the past you would have to create multiple libraries for each target framework (such as iOS or Android) that you wanted to support if there was platform specific code. With the new SDK style projects you can set the TargetFrameworks
property and specify as many as you would like.
Here is Xamarin.Essentials that supports iOS, Android, UWP, and .NET Standard 1.0/2.0:
<TargetFrameworks>netstandard1.0;netstandard2.0;Xamarin.iOS10;MonoAndroid71;MonoAndroid80;MonoAndroid81;uap10.0.16299</TargetFrameworks>
What is amazing is that all of your files can live together and can be conditionally compiled so only certain files get compiled for a specific platform:
<ItemGroup>
<Compile Include="**\*.shared.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
<Compile Include="**\*.netstandard.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
<Compile Include="**\*.uwp.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
<Compile Include="**\*.android.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
<Compile Include="**\*.ios.cs" />
</ItemGroup>
This allows super beautiful project structures:
It also means that you can conditionally compile your code too:
#if NETSTANDARD1_0
// do something for .net standard 1.0
#elif __ANDROID__
// else for Android
#else
// anything else.
#endif
This all works thanks to Oren Novotny's amazing library MSBuild SDK Extras, which is easily configured by adding a nuget to all projects and adding a single import at the bottom of your csproj:
<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.6.47" PrivateAssets="All" />
<Compile Include="**\*.shared.cs" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
Enter .NET Core SDK 2.1
Since this is sort of "unofficial" special tooling magic, sometimes things can break and recently when I installed .NET Core SDK 2.1 all my files went missing for iOS/Android/UWP. If we toggle on "show all files" they are totally there and get compiled but now intellisense stops working:
We reported it and worked with the .NET team on how to resolve it.
Fix 1: Include Workaround
The work around from the .NET team was to add an additional include at the bottom of our csproj that basically includes any .cs files and excludes defaults.
<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.6.47" PrivateAssets="All" />
<Compile Include="**\*.shared.cs" />
</ItemGroup>
<ItemGroup>
<None Include="**\*.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
Fix 2: Update to 1.6.47 of Sdk Extras & Project Type
I work extremely closely with Oren and whenever something happens with Essentials I freak out :) So when we had Fix 1 in place and working the next natural thing was to roll it into the official NuGet! Last week Oren did just this!
The only thing here is that we will want to ensure we upgrade to his new project type instead of using the PackageReference and Import. He outlines it great in his documentation.
In short instead of adding any nugets or imports we will replace:
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
with
<Project Sdk="MSBuild.Sdk.Extras/1.6.47">
Of course if you are using other types of files you may need to go back to Fix 1 to add them in manually, but his system is very smart. Be sure to report any bugs over to Oren on his GitHub repo.