Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
279 views
in Technique[技术] by (71.8m points)

c# - How does one create a project to create a Nuget package for Xamarin Forms supporting iOS, Android, and UWP?

It feels like no one in the world has done this. I can't find any tutorials or help on how to create this. What I'm trying to do is create my own plugin, let's say to add notifications to Xamarin forms, that will work on Android, iOS, and UWP. Every mention of this I see mentions to create a multiplatform library but this is only available for VS for Mac and does NOT include UWP. VS2017 does not include a multiplatform library template. I want it to work like all the other xamarin plugins where you download it from nuget into all your projects and you have access to platform implementations and shared code.

I see other questions on stackoverflow that ask this but they are not answered or have links that are dead and don't work on VS2017.

How can I create a xamarin forms library that can be shared between multiple projects? How can I make this into a nuget package so it is one brainless install and it just works? The more detailed the better as it will help everyone that I saw googling this issue and found no answer.

Remember this is NOT for visual studio Mac like the microsoft tutorials show, but for VS 2017 community edition with the latest updates.

Edit 1:

Just to add to some of the comments below, I also tried this plugin that James created and that even doesn't work the way it does in his youtube video: https://www.youtube.com/watch?v=MSwH8NrtVCk&feature=youtu.be

The resulting project doesn't have any folders.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is exactly what MvvmCross, ReactiveUI and other frameworks do. So claiming no one in the world has done this is some kind of wild imagination.

One way to do it is to create a Multi-Target library, just be aware that Visual Studio for Mac doesn't like those projects yet and you will have issues creating and developing such a library there. However, if you only develop on Windows Visual Studio 2017 and newer supports these kind of projects very well.

You can start off by creating a .NET Standard library in VS, which will give you a project with a single .cs file containing Class1, go a head and remove that.

If you edit the .csproj file you will see a PropertyGroup in the top of the file containing a property called TargetFramework. Change that to TargetFrameworks and list all the desired frameworks you want to target:

<PropertyGroup
    <TargetFrameworks>netstandard2.0;Xamarin.iOS10;MonoAndroid81;uap10.0.16299</TargetFrameworks>

If you want easy support in Multi-Target libraries, I advise you to switch to MSBuildSDKExtras for your project SDK, as it will handle a lot of the quirks that comes with targeting Xamarin platforms. It will add necessary properties to your project to make all of this work.

So first line of the .csproj file will have to look like:

<Project Sdk="MSBuild.Sdk.Extras/1.6.68">

Now you need to specify in which folders your platform specific code is in. What I usually do is to create a platforms folder with android, ios and uap folders and consider the rest of the code platform independent and residing in common.

<ItemGroup>
    <Compile Remove="platforms***.cs" />
    <None Include="platforms***.cs" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
    <Compile Include="platformscommon***.cs" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
    <Compile Include="platformsios***.cs" />
    <Compile Include="platformscommon***.cs" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
    <Compile Include="platformsandroid***.cs" />
    <Compile Include="platformscommon***.cs" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('uap')) ">
    <Compile Include="platformsuap***.cs" />
    <Compile Include="platformscommon***.cs" />
</ItemGroup>

To nicely create NuGet packages from this Multi-Target library, I usually add a Directory.build.props file in the root of the solution where I specify properties for the Packages:

<Project>
  <PropertyGroup>
    <Company>Cheesebaron</Company>
    <Copyright>Copyright ? Cheesebaron</Copyright>
    <RepositoryUrl>https://github.com/cheeesebaron/cool-stuff</RepositoryUrl>
    <Authors>Cheesebaron</Authors>
    <Owners>Cheesebaron</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>

Also I add a Directory.build.targets file defining the constants for the different platforms:

<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>ResourcesResource.designer.cs</AndroidResgenFile>
  </PropertyGroup>
</Project>

If you are targeting Android, you will probably also need to add:

<None Include="Resources*.cs" />
<Compile Remove="Resources*.cs" />

To the first ItemGroup in your .csproj file and add something like:

<AndroidResource Include="Resources***.xml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />
<AndroidResource Include="Resources***.axml" SubType="Designer" Generator="MSBuild:UpdateAndroidResources" />

To the Android ItemGroup to properly update Android Resources.

Adding files in Visual Studio can be a bit tricky and it will often add them in the wrong place for the first couple of files until you have something in all of the platform specific folders. But you shouldn't need anything else in the .csproj file as long as the files are placed correctly in the structure we've defined.

You can see examples of Multi-Target libraries here:

https://github.com/MvvmCross/MvvmCross/blob/develop/MvvmCross/MvvmCross.csproj https://github.com/MvvmCross/MvvmCross/blob/develop/Directory.build.props https://github.com/MvvmCross/MvvmCross/blob/develop/Directory.build.targets

And similarly for ReactiveUI:

https://github.com/reactiveui/ReactiveUI/blob/master/src/ReactiveUI/ReactiveUI.csproj

As of now it is not super easy to get started, but is fine to work with. Alternatives to this are .NET Standard libraries and platform specific libraries adding the Bait-and-switch pattern.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...