Uno Platform

6/19/2018

Uno Platform is likely the best platform for developing apps targeting multiple platforms - Windows, web, Android and iOS.

Uno Platform is likely the best platform for developing apps targeting multiple platforms - Windows, web, Android and iOS.

One should have realistic perspective of Uno before getting into it:

  1. Uno is rapidly evolving and improving. Not all UWP APIs are supported by Uno. 
  2. Suppose a project targeting multiple platforms could save 1000 hours by using Uno if all UWP APIs were supported, but in reality the project would save 800 hours because 200 hours would be used to find ways to get around those unsupported APIs (it usually is not difficult at all).
  3. If you are a perfectionist expecting everything should work exactly as documented, do not use Uno.   
  4. If one is realistic, it can be very pleasant and productive in developing apps with Uno by taking full advantage of the IDE king - Visual Studio, and the beauty of Xaml.  For example, NavigationView is supported, but not working consistently X-platform.  Instead of spending time tweaking it, one can easily create a simple navigation mechanism with other basic controls such as Grid especially if the navigation view is primarily needed for the home page.  

To start enjoying Uno Platform:

  1. Join Uno Gitter,  well supported must-have lifeline at this stage of Uno.
  2. Install the Uno Solution Template Visual Studio Extension (UnoSolutionTemplate.vsix)

For each new app tarting multiple platforms:

  1. Create a new C# solution using the Uno App Solution template.  This results in 5 project folders: Droid, iOS, Shared, UWP Wasm.
  2. Update NuGet Packages. Use the latest prerelease Uno NuGet packages. Do not update Microsoft.Extensions.Logging packages.   
  3. Set the UWP target to Build 18362, and Min to Build 16299.
  4. (no longer needed.  It is done automatically)Add the Shared folder to the references of the other 4 projects.
  5. Build the UWP project (required for Xaml IntelliSense)
  6. Do not reference any .NET Standard 2.0 class library.  Create a Uno class library if needed as described later.
  7. Do not use "Binding", instead, use "x:Bind" for data binding.  
  8. Do as much as possible in the Shared project.
  9. Use the UWP project (top left corner) for Xaml editing.

UWP Head:

  1. Associate the app with Microsoft Store.
  2. Edit Assembly Information of the project properties.
  3. Edit Package_appxmanifest.xml.  Change Entry point to: MyAppNameSpace.app, select Lock screen notification for the Application tab.  Populate the Visual Assets tab.  
  4. Create the store package and use Windows App Certification Kit to validate it.
  5. Submit the package to Microsoft Store.

Android Head:

  1. Change ApplicationName in Resources > values > Strings.xml to your desired app name. 
  2. Change Assets\Resources\drawable\icon.png from the default Android icon to the app icon.
  3. Properties > Android Manifest: populate the fields properly. Set the Target Android version to a specific version.  Do not use the default value "Use Compile using SDK version" 
  4. Properties > Android Package Signing: use an existing keystore or use Android Studio to create a keystore, then use it.
  5. Select "Any CPU" configuration to deploy the app to ether physical devices or emulators.
  6. Deploy the app to an Android device to generate Android packages (package_name.apk and package_name-Signed.apk) under MyApp.Droid\bin\Release. Build or Rebuild does not create packages.
  7. Upload package_name-Signed.apk to Google Play for publication.

WASM Head (for testing with IIS):

  1. Add the following to code to  OnNavigatedTo() to change the tile for WASM. Adding it to the constructor of App will not work. The WASM title dipslayed by a browser would be "Free 0.0.0.0" without the following:
    • #if __WASM__
      Windows.UI.ViewManagement.ApplicationView appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
      appView.Title = PCLUtility.sAppName;
      #endif
  2. Copy the working web.config and index.html from an existing WASM head to the new one's wwwroot folder.
  3. Add an application to a local web site in IIS and set its physical path to: ...\MyApp\MyApp.Wasm\bin\Debug\netstandard2.0\dist 
  4. Add MIME type "application/octet-stream .clr"to IIS.
  5. Add MIME type "application/wasm .wasm" to IIS.
  6. Add MIME type "application/woff2 .woff2" to IIS
  7. Add MIME type "application/pdb .pdb" to IIS
  8. Run http:localhost/Myapp for testing.  It usually takes 10+ seconds to load. It works with Chrome, Opera and Firefox.  Microsoft Edge does not support localhost. Wasm cannot be started from Visual Studio.
  9. Build the release version after debugging, and upload the contents of folder "...\MyApp\MyApp.Wasm\bin\Release\netstandard2.0\dist" to a web server, then set a navigation link to the folder or index.html of the folder. Add the aforementioned MIME types to the server's IIS.

 WASM AoT building.

  1. Follow the instructions
  2. Create buildmyapp.sh with the following contents under: ...\rootfs\home\unix
    source ~/emsdk/emsdk_env.sh
    cd `wslpath "C:..\MyApp\MyApp.Wasm"`
    msbuild /r /p:Configuration=Release
  3. Run $chmod +rwx buildmyapp.sh to grant all permissions to this file.   Please note that "$chmod +x" is supposed to work, but actually not.
  4. Create buildmyapp.bat with the following contents:
    1. bash -ic "cd ~ ; ./buildmyapp.sh"
  5. Run buildmyapp.bat every time when building the AoTed release version.

The following is needed in .csproj file to get around AoT limitations:

<ItemGroup>
<MonoRuntimeMixedModeExcludedAssembly Include="SkiaSharp.Views.Uno" />
<MonoRuntimeMixedModeExcludedAssembly Include="SkiaSharp.Wasm" />
<MonoRuntimeMixedModeExcludedAssembly Include="SkiaSharpSample.Wasm" />
<MonoRuntimeMixedModeExcludedAssembly Include="Newtonsoft.Json" />
<MonoRuntimeMixedModeExcludedAssembly Include="System.Net.Http" />
</ItemGroup>

Create a library that can be referenced by all Uno heads:

  1. Create it using Cross-Platform Library (Uno Platform) template.
  2. Add Newtonsoft.Json NuGet package for using Json.
  3. Add Microsoft.CSharp NuGet package for using dynamic.
  4. Unload the library project.
  5. Edit this library project's .csproj file.
  6. Following the workaround as of 2018-11-27
  7. Add the following to the .csproj file to use DataContractSerializer and XElement:

    <ItemGroup>
        <Reference Include="System.Runtime.Serialization" />

         <Reference Include="System.Xml.Linq" />

    </ItemGroup>

  8. Save this .csproj file, load and build the project.
  9. Check NuGet package MSBuild.Sdk.Extras periodically and update .csproj file accordingly. It cannot be managed by NuGet package manager. 

 Reference the library:

  1. Add the library as a reference to every head.
  2. Copy all necessary image files from the library's Assets folder to the Assets folder of the shared head.  This is because the images of the library are not copied to the final Assets folder of the WASM head.  If a library page has an Image control referencing an image in the Assets folder, it would not be able to find it without this copying. 

 Add a UserControl derived control to Uno class library:

  1. Right click Uno class library project > Add > new item > Uno Platform > Blank Page.
  2. Go to the newly created Xaml page and replace Page with UserControl.
  3. Go to the newly created C# class file and replace Page with UserControl.

Both Pages and userControls can reference image files in a folder of the library, it does not work for WASM.   All referenced files need to be copied to the corresponding folder of the shared folder of a project.

For analytics and ads, use the official Xamarin packages for non-WASM heads.

For sockets, use .Net sockets for non-WASM heads.

 

Initial method to create a library that can be referenced by all Uno projects:

  1. Create a .NET Standard Class library.
  2. Unload the library project.
  3. Edit the this library project's .csproj file.
  4. Copy the contents from this demo file.
  5. Remove the <Page> element for "Themes\Generic.xaml"
  6. Ensure the version in <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform " Version="6.1.5" /> is the latest version of Microsoft.NETCore.UniversalWindowsPlatform.  Normal NuGet update of Microsoft.NETCore.UniversalWindowsPlatform does not work for this library. 
  7. Ensure the uap version is at least uap10.0.16299 in <TargetFrameworks> so that NuGet packages such as Newtonsoft.Json can be installed properly. 
  8. Change this line <ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0' "> to <ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0.16299' "> to avoid "Error CS0012 The type 'Object' is defined in an assembly that is not referenced."
  9. Add the following to the .csproj file to use DataContractSerializer and dynamic:

    <ItemGroup>
    <Reference Include="System.Runtime.Serialization" />

    <Reference Include="Microsoft.CSharp" />
    </ItemGroup>

  10. Save this .csproj file, load and build the project.
  11. Install NuGet package Microsoft.CSharp

 For imaging work, there is an outstanding review for the options. SkiaSharp NuGet package is a great choice.

Unsupported:

  • Implicit style.
  • Microsoft Advertising SDK for XAML.
  • Toast functions.
  • Windows.Security.Cryptography. (replacement System.Security.Cryptography of .Net Standard). 
  • FrameworkElement
  • IStorageFolder
  • EmailMessage
  • Windows.ApplicationModel.DataTransfer.DataPackage 
  • DataTransferManager
  • System.Math.Log10
  • System.Math.Ceiling
  • NavigationView
  • BitmapEncoder

 WASM performance is the major issue of UNO.  It takes more than 10 seconds to load 

Modernize Uno WASM head:

  1. Use an existing modern Uno project or create a modern project as the template.
  2. Compare Program.cs and replace Main().
  3. Copy Properties of the modern project to the old one, and change the 2 port number to in launchSettings.json to unique ones (i.e. 55835 to 55837, 55836 to 55838).
  4. Compare .cspoj files: Replace Microsoft.NET.Sdk with Microsoft.NET.Sdk.Web.  Add <DotNetCliToolReference Include="Uno.Wasm.Bootstrap.Cli" Version="1.0.0-dev.300" />.
  5. Compare LinkerConfig.xml and make changes accordingly. 
  6. Copy folder wwwrroot
  7. Copy folder Assets\Fonts of the Droid header.