Create multi-target Xamarin.Forms NuGet package - c#

I'm trying to build a single NuGet package that can be installed in Xamarin.Android, Xamarin.iOS, Xamarin.UWP and Xamarin.Forms (.NET Standard) projects. I can't use .NET Standard since I need to have a custom Activity for my Android project, custom AppDelegate for my iOS project, and a custom MainPage for my UWP project. In the initiators I want to use the ServiceCollection as DI provider.
In the docs they still mention the Portable Class Library (explicitly mentioned legacy, doesn't exist anymore in VS), or Manually Creating Packages (can't figure out what to do here), and then you also have websites mentioning the Xamarin.Forms Plugin, which also doesn't exist anymore.
I already made a solution with all 4 projects, but I can't figure out how I can create a single NuGet package for all 4 projects (all 3 target platforms).
Does anyone have an idea how we can build a multi-target NuGet package containing code for Android, iOS, and UWP, just like the Xamarin.Forms NuGet package?
I've also seen discussions like this: How does one create a project to create a Nuget package for Xamarin Forms supporting iOS, Android, and UWP?, but I'm not sure if this is still relevant, since the build.props and build.targets no longer exist in the MvvmCross repo.

On starting point is https://github.com/onovotny/MSBuildSdkExtras
A year ago, I made a sample (and collected some documentation and references), which might be inspiring for this subject: https://github.com/ZeProgFactory/MSBuildSdkExtrasTest
and https://github.com/ZeProgFactory/MediaPlayer is using it
And definitively, you should look at the repositories of James Montemagno's components at https://github.com/jamesmontemagno
With these references you should be able to start. But all of these had a mayor difference with your approach:
they are using one project (via MSBuildSdkExtras),
which is builded for all the platforms
and, finally, the binaries are assembled in one NuGet.
Perhaps you may take only this last step. Anyway, this approach is at least an option.
Hope this helps …

I've pushed a working version to https://github.com/MintPlayer/MintPlayer.MVVM
csproj-file
<Project Sdk="MSBuild.Sdk.Extras/2.0.41">
<!-- You must have the Android 8.0 SDK installed through the Android SDK manager -->
<PropertyGroup>
<AssemblyName>MintPlayer.MVVM</AssemblyName>
<RootNamespace>MintPlayer.MVVM</RootNamespace>
<TargetFrameworks>netstandard2.0;Xamarin.iOS10;MonoAndroid80;uap10.0.16299</TargetFrameworks>
<_WriteTelemetryProperties>false</_WriteTelemetryProperties>
<Authors>Pieterjan De Clippel</Authors>
<Company>MintPlayer</Company>
<Product>MintPlayer.MVVM</Product>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Description>This package allows you to implement ViewModel Navigation and Dependency Injection in a Xamarin.Forms project</Description>
<Version>1.0.0</Version>
<Copyright />
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/MintPlayer/MintPlayer.MVVM</PackageProjectUrl>
<RepositoryUrl>https://github.com/MintPlayer/MintPlayer.MVVM</RepositoryUrl>
<PackageTags>Xamarin.Forms, Viewmodel navigation, Dependency Injection</PackageTags>
<PackageReleaseNotes>This package is still under construction</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Platforms\**\*.cs" />
<None Include="Platforms\**\*.cs" />
<None Include="Resources\*.cs" />
<Compile Remove="Resources\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap')) ">
<Compile Include="Platforms\UAP\**\*.cs" />
<Compile Include="Platforms\Common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
<Compile Include="Platforms\Common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
<Compile Include="Platforms\iOS\**\*.cs" />
<Compile Include="Platforms\Common\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
<Compile Include="Platforms\Android\**\*.cs" />
<Compile Include="Platforms\Common\**\*.cs" />
<AndroidResource Include="Resources\**\*.xml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />
<AndroidResource Include="Resources\**\*.axml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />
</ItemGroup>
<ItemGroup>
<None Remove="Platforms\Common\MintPlayerMvvmExtensions.cs" />
<None Remove="Platforms\Common\NavigationService.cs" />
<None Remove="Platforms\Common\Platform.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
<!--<PackageReference Include="Xamarin.Forms" Version="4.5.0.495" />-->
<PackageReference Include="Xamarin.Forms" Version="3.1.0.697729" />
</ItemGroup>
</Project>
Directory.build.props
<Project>
<PropertyGroup>
<Company>MintPlayer</Company>
<Copyright>Copyright © MintPlayer</Copyright>
<RepositoryUrl>https://github.com/MintPlayer/MintPlayer.MVVM</RepositoryUrl>
<Authors>Pieterjan De Clippel</Authors>
<Owners>MintPlayer</Owners>
<PackageReleaseNotes />
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryType>git</RepositoryType>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<NeutralLanguage>en</NeutralLanguage>
<LangVersion>latest</LangVersion>
<NoWarn>$(NoWarn);1591;1701;1702;1705;VSX1000;NU1603</NoWarn>
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' and '$(IsTestProject)' != 'true'">true</GeneratePackageOnBuild>
<Platform>AnyCPU</Platform>
<DebugType>portable</DebugType>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IsTestProject>$(MSBuildProjectName.Contains('UnitTests'))</IsTestProject>
</PropertyGroup>
</Project>
Directory.build.targets
<Project>
<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
<DefineConstants>$(DefineConstants);NETSTANDARD;PORTABLE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('Xamarin.iOS'))">
<DefineConstants>$(DefineConstants);MONO;UIKIT;COCOA;IOS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('MonoAndroid'))">
<DefineConstants>$(DefineConstants);MONO;ANDROID</DefineConstants>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
</PropertyGroup>
</Project>
You must add the following to your .sln file
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7004E39A-BAF2-4F2F-B505-CC3DEC393CB6}"
ProjectSection(SolutionItems) = preProject
Directory.build.props = Directory.build.props
Directory.build.targets = Directory.build.targets
EndProjectSection
EndProject

Related

What is the difference between <UseMauiEssentials> and <UseMaui> in the Android/iOS csproj file? Should I use both in my csproj files?

I want to use .NET MAUI in my existing Android and iOS projects but I'm not sure if I need to add UseMauiEssentials and UseMaui or only one of them to my existing csproj files. Currently I use Xamarin.Essentials and some other Xamarin NuGet packages in my Android and iOS projects.
My Android csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>23</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<ApplicationId>com.companyname.AndroidprojectwithXamarin</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
<PackageReference Include="MonoGame.Framework.Android" Version="3.8.1.303" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.4" />
</ItemGroup>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High" />
<Exec Command="dotnet tool restore" />
</Target>
</Project>
My iOS csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-ios</TargetFramework>
<OutputType>Exe</OutputType>
<SupportedOSPlatformVersion>15.0</SupportedOSPlatformVersion>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<CreatePackage>false</CreatePackage>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<CreatePackage>false</CreatePackage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
<PackageReference Include="MonoGame.Framework.iOS" Version="3.8.1.303" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.4" />
</ItemGroup>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High" />
<Exec Command="dotnet tool restore" />
</Target>
</Project>
What is the difference between UseMauiEssentials and UseMaui in the Android/iOS csproj files? Should I use both in my csproj files?
Is it necessary to remove the Xamarin.Essentials NuGet package from my projects? Is it necessary to add another .NET MAUI NuGet package if I want to use .NET MAUI? Or should I leave the Xamarin.Essentials NuGet package installed?
As you've already found, Xamarin.Essentials was a separate project. Essentials was basically an abstraction layer for all kinds of APIs that are available across the different platforms (iOS, Android, Windows) but don't necessarily have any UI.
Since Xamarin.Forms was a UI framework, all of Essentials was not included in Forms. In fact, you can use Xamarin.Essentials with a traditional Xamarin application if you want, there is no dependency on Forms.
With the introduction of .NET MAUI, Essentials as a separate concept kind of went away. Essentials is now an integrated part of .NET MAUI and it's just APIs that are available for you to use.
However, those APIs still have no dependency on .NET MAUI and you should be able to use Essentials without using .NET MAUI. And that is where we get to the answer of this question: use UseMauiEssentials if you just want to use the APIs formerly known as Essentials, or, if you're going to use all of .NET MAUI anyway, just include UseMaui that will also bring in Essentials automatically.

Project reference in Nuget

I need to create a Nuget of project A which is dependent on project B (not a nuget project, local one).
I have added Project B as project dependency to project A and enabled property to generate package on build. It is creating nuget package file but not including all the files from project A.
I tried few things and google also but not finding much help.
I found one similar question Build NuGet Package automatically including referenced dependencies
but not working. I am able to create pkg but with few files copied over. while I can see all the files in debug folder. No idea on what basis Nuget is picking few files from project A.
Can anyone tell me what's wrong here.
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>1.0.0.0-alpha</PackageVersion>
<Platforms>x64</Platforms>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<PlatformTarget>x64</PlatformTarget>
<NoWarn></NoWarn>
<WarningsAsErrors />
<OutputPath>bin\Debug</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<NoWarn></NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<OutputPath>bin\Release</OutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Configuration" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="someProject.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>all</IncludeAssets>
</ProjectReference>
</ItemGroup>
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))"/>
</ItemGroup>
</Target>
</Project>

use .net core sdk in MSBuild project

I have a PCL-project with different functions and classes for each platform. I want to implement .net core support now. But I cant use controls like UserControl because the Microsoft.NET.Sdk.WindowsDesktop SDK isn't referenced. The .net framework is easy to implement because I only have to reference each assembly... But in .net core, I can't reference the assembly...
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;xamarin.ios10;xamarin.mac20;xamarin.tvos10;monoandroid10.0;tizen40</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">uap10.0.17763;net472;netcoreapp3.1;$(TargetFrameworks)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) And '$(OS)' == 'Windows_NT' ">
...
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.Xaml" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netcoreapp3')) And '$(OS)' == 'Windows_NT' ">
...
<SDKReference Include="Microsoft.NET.Sdk.WindowsDesktop" />
</ItemGroup>
That's my executable app, referencing the PCL-project above;
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference ...... />
</ItemGroup>
</Project>
I already tried this to reference the SDK but its not working.
<SDKReference Include="Microsoft.NET.Sdk.WindowsDesktop" />
I want to implement .net core support now. But I cant use controls
like UserControl because the Microsoft.NET.Sdk.WindowsDesktop SDK
isn't referenced. The .net framework is easy to implement because I
only have to reference each assembly... But in .net core, I can't
reference the assembly..
After doing a deep research, I found that Microsoft.NET.Sdk.WindowsDesktop cannot be used by SDKReference.
As a suggestion, you could create a custom targets file and then import it into your PCL-project to use the Net Core SDK.
1) create a file called custom.targets in your PCL project folder.
2) Then add these in custom.targets:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
3) import this in xxx.csproj file of PCL-Project.
<Import Project="$(ProjectDir)custom.targets" Condition=" $(TargetFramework.StartsWith('netcoreapp3')) And '$(OS)' == 'Windows_NT' "/>
4) Then restart your project. Although there are some warnings that reminds you that some sdks are repeatedly quoted, you can ignore them and it will not have any impact on your project.
You can check this, which works well in my side.

Add resource to Xamarin.Forms NuGet

I'm about to write a custom NuGet for Xamarin.Forms projects and want to include some resources (font awesome otfs) in the NuGet so the referencing projects can use them.
I've managed it to copy the files from the NuGet to the Android assets folder and mark them as assets file correctly.
However I'm not able to include them in iOS as resource files...
Did anyone know how to do that? Or how to include resources at all (with a NuGet).
Here is my code snippet from the csproj file (NuGet) that works for android but not for iOS:
<PropertyGroup Condition=" '$(Configuration)'=='Debug' ">
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<!-- If config is release create a package -->
<PropertyGroup Condition=" '$(Configuration)'=='Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- sourcelink: Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<ItemGroup>
<Folder Include="fonts" />
<FontFiles Include="fonts\*.otf" />
</ItemGroup>
<ItemGroup>
<None Remove="#(FontFiles)" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('xamarin.ios')) ">
<Compile Include="ios\**\*.ios.cs" />
<TfmSpecificPackageFile Include="#(FontFiles)" BuildAction="BundleResource" PackagePath="contentFiles\any\$(TargetFramework)\Resources\" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('monoandroid')) ">
<Compile Include="android\**\*.android.cs" />
<Folder Include="Assets\" />
<AndroidAsset Include="#(FontFiles)" Link="Assets\%(Filename)%(Extension)" />
</ItemGroup>

Conditional package reference in UWP project

I would like to use one NuGet package just for Debug configuration. I found possibility to do it in Visual Studio 2017 if I have a UWP project targeting Creators Update (15063).
<PackageReference Include="Newtonsoft.json" Version="9.0.1" Condition="'$(Configuration)' == 'Debug'" />
But the package is still there also for Release configuration.
<Choose>
<When Condition=" '$(Configuration)'=='Debug' ">
<ItemGroup>
<PackageReference Include="Newtonsoft.json" Version="9.0.1" />
</ItemGroup>
</When>
</Choose>
The PackageReference has to be in an ItemGroup than it works.
Currently, you cannot condition on Configuration. Please file a feature ask on NuGet GitHub repo.
The only supported condition is TargetFramework
You can use Choose/When as a workaround:
<Choose>
<When Condition=" '$(Configuration)'=='Debug' ">
<PackageReference Include="Newtonsoft.json" Version="9.0.1" />
</When>
</Choose>

Categories