Thursday, April 10, 2014

Universal Apps: What Are They And Can I Use Them With Xamarin?

At Build this year the new Universal App type was announced that allows you to write apps on for both the Windows Store and Windows Phone 8.1 platforms with one code base.  There were also hints that this would be supported in Xamarin as well.  Exactly how these work was a little unclear to me so I downloaded the Visual Studio 2013 update 2 RC and did some experimentation, at least what is possible in the release candidate.  The Visual Studio 2013 Update 2 RC can be found here:

I opened up the new project window and with a little searching around this is what I found:

There are four new project types:

  • Blank App (Universal App) - Very simple solution to create two projects (Windows Store and Windows Phone 8.1) and one shared universal project with a nifty new icon
  • Hub App (Universal App) - Slightly more complex than the Blank app, it still creates a Windows Store and Windows Phone 8.1 projects both using the same shared project
  • Class Library (Portable for Universal Apps) - Creates a project to create a Portable Class Library (DLL) in the profile that supports Windows Store 8.1 and Windows Phone 8.1 applications.  Currently there are no profiles that support Windows Phone 8.1 and Xamarin so I was not able to make one of these that also supports Xamarin but I suspect this will be updated soon enough.  This project type is intended to create libraries that can be shared outside the solution.
  • Windows Runtime Component (Portable for Universal App) - Similar to the Class Library type except compiles into a winmd file instead of a dll.

Blank App (Universal App) - This creates a new universal app solution with three projects, One for a Windows Phone 8.1 UI, one with a Windows Store Application and one shared universal application which would seem to be where the interesting bits are.

The new shared app type just shows up with an xaml file and is referenced by both the Windows Store application and the Windows Phone 8.1 application.  This is a project type unlike what you may be used to, in fact it appears to be only a repository for shared code that can be used by other projects in the solution.  Here are some things that are of interest:

  • If you right click on the shared universal project and select properties all you can change is the root namespace.  That's it.
  • When compiled no DLL is generated for this project, all code in it is compiled directly into the Windows Store and Windows Phone 8.1 applications.  These projects cannot be independently be shared or used outside of the projects that directly reference them in a solution.
  • You cannot compile only the Shared project on the build menu, it cannot be built independently.  This makes sense give that it has no output and is compiled directly into a referencing project.
  • As of the time of the writing there is no references nor ability to add references to the shared project through the Visual Studio UI.  What is available to you within code of these projects is the subset of the .NET framework for this project and that's it.  That means no referencing Autofac or any third party library in code through the Visual Studio UI. 
  • EDIT TO PREVIOUS VERSION - The shared project *WILL* pickup references in the projects that reference them.  You have to select the proper project in the upper left dropdown when editing a file in the shared project and it will pickup the proper references.

Here is a screenshot of the project selection dropdown where you can select the right platform specific project and get the proper references and conditional compilation symbols:

I wasn't satisfied with the last point as it would be extremely limiting so I decided to do some experimentation.  I went out to Nuget and added the latest stable version of Windows Azure Mobile Services to the Windows Store and Windows Phone 8.1 projects.  I then opened up the Windows Store project file in Notepad and copied the following code to the shared project file from the Windows Store project file:

  <Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\..

\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
  <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
    <Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This 

project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more 

information, see" HelpKeyword="BCLBUILD2001" />
    <Error Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build 

restored NuGet packages. Build the project again to include these packages in the build. For more information, see" HelpKeyword="BCLBUILD2002" />

When I refreshed the shared project I still had no references section visible in the project on the Visual Studio UI.  However, I then created a new class in the Shared project with the following code:

using Microsoft.WindowsAzure.MobileServices;

namespace App1
    class ReferenceTest
        private MobileServiceClient Test1()
            return new MobileServiceClient("");

Granted this code is rather useless but Visual Studio now recognized that Microsoft.WindowsAzure.MobileServices is valid in the shared project.  I was able to compile both projects using the shared project just fine.  So there may not be support in the Visual Studio UI for this at the present time but it does appear possible to reference other assemblies in your shared project.  Of course when I removed the Windows Azure Mobile Services reference from my Windows Phone 8.1 project, it no longer compiled though the Windows Store project continued to compile so any references added to the shared project also need to be added to any project that reference it, which makes perfect sense.

How about Xamarin?  As I stated before I couldn't create a Portable Class Library for Universal Apps that also referenced either Xamarin iOS or Android as there did not seem to be a profile for that combination.  What about the shared project?  I went ahead and added Xamarin iOS and a Xamarin Android UI projects to the solution.  I then went to the references section of the Android project and tried to add a reference to App1.Shared like there was in the Windows Store and Windows Phone 8.1 projects.  No luck, that wasn't an option when looking at the solution nor could I browse out to a DLL because one does not exist.

I did, however, find the following blog post that states how to modify the project file:

I took the advice and added the following code to my Android project file in Notepad (As noted in the blog, be careful about the path being correct or your project will not load):

<Import Project="..\App1\App1.Shared\App1.Shared.projitems" Label="Shared" />

When I then reloaded the Android project as the blog post indicated I suddenly has a reference to the shared universal app project.

When I tried to compile I couldn't for two reasons.  First the default Blank App added a Xaml file to the shared app, App.xaml. Currently xaml isn't going to be usable by the Android or iOS projects and so can't exist in App1.Shared.  I moved this to the individual Windows Store and Windows Phone 8.1 applications (I could also have used pre-compilation directives so it wouldn't be visible to the Android project to the same effect). 

The second reason is because the shared project uses the Windows Azure Mobile Services component and my Android project does not reference it.  I added this and the problem was resolved (Note: sometimes when getting the Windows Azure Mobile Services package for Xamarin from Nuget it drags along some extra system DLLs that I have to delete: System.IO, System.Runtime and System.Threading.Tasks).

With these issues resolved I am able to use the shared project in Xamarin Android.  By following the same steps I am able to do it with the Xamarin iOS project as well.

In conclusion, what have I learned about the present state of Universal Apps and Xamarin?

  • Universal PCLs and winmd projects won't work with Xamarin at the present time because there is no profile that supports Windows Phone 8.1 and Xamain project types.  I expect that to be resolved.
  • Creating a Universal Blank App or Hub app creates a shared project type
    • The shared project cannot be created directly through the Visual Studio UI and instead must be created through one of those two project types
    • Code in the shared project does not create a DLL or winmd file but instead is compiled directly into the projects that reference the project
    • There is no way through the Visual Studio UI to add a reference to an external dll to a shared universal project but references can still be added by editing the project file directly
    • You do not need references in the shared project because it picks up references form the projects that use it.  When in a code file you can switch between projects to pick up their references and conditional compilation constants
    • All projects that reference the shared universal project must reference and be able to use any code in the shared project or measures such as pre-compilation directives must be used
    • Xamarin iOS and Android UI projects can use the shared project but only if their project files are edited directly as there is not currently any way to do it through the Visual Studio UI

In general the shared universal projects would seem to be a new alternative to the old method of simple linking a shared code file between two or more different projects.  As is common with that method platform specific differences can be handled via pre-compilation directives.  Any sharing of the UX may work on projects that use xaml but of course such code is not currently workable in Xamarin Android and Xamain iOS and would have to be hidden from those project types.

UPDATE: If you want to create a PCL today that targets Windows Phone 8.1 and Xamarin Android and/or Xamarin iOS @danrigby has a great blog post that explains how to do it until an update is released:

1 comment:

  1. I updated the post based on new experimentation with universal applications and understanding how references work. Thanks!